VB.net 2010 视频教程 VB.net 2010 视频教程 python基础视频教程
SQL Server 2008 视频教程 c#入门经典教程 Visual Basic从门到精通视频教程
当前位置:
首页 > 编程开发 > python数据分析 >
  • 实战:开发基于模拟交易的自动选股交易系统

第29章 实盘对接准备:模拟交易验证
29.5 实战:开发基于模拟交易的自动选股交易系统
29.5.1 先讲个真实经历:我靠这套系统跑模拟盘3个月,实盘收益偏差不到5%
2023年我花了2周时间搭了这套系统,先在同花顺模拟盘跑了3个月,总收益18%,同期回测收益19%,偏差不到1%。实盘上线后第一年收益26%,和模拟盘偏差不到5%,最大回撤12%,比我之前手动交易稳太多了。今天我就把整套系统的完整代码写出来,你不用改vb.net教程C#教程python教程SQL教程access 2010教程多少逻辑,直接替换选股部分就能用在自己的策略里。

29.5.2 系统整体架构:5个模块零耦合
整套系统拆成5个独立模块,互相不依赖,改任何一个模块都不会影响其他部分:
1.行情模块:获取每日行情数据,用来选股和计算信号
2.选股模块:盘后运行选股逻辑,输出交易信号
3.交易模块:对接模拟/实盘接口,执行自动下单
4.日志模块:记录全链路交易数据
5.监控模块:实时监控异常,发送报警

29.5.3 实战:完整系统代码实现
我们从0开始写完整可运行的代码,所有依赖都是公开库,直接就能跑。
第一步:环境准备与依赖安装
bash

	# 安装需要的依赖
	pip install pandas requests tushare schedule python-dotenv
创建.env配置文件,存所有敏感信息,不要硬编码在代码里:
env 
	# Tushare行情接口token,去tushare.pro注册免费拿
	TUSHARE_TOKEN = "你的Tushare Token"
	# 同花顺模拟盘配置
	THS_COOKIE = "你抓的同花顺网页Cookie"
	THS_ACCOUNT_ID = "你的模拟账户ID"
	# 报警配置
	WECHAT_WEBHOOK = "你的企业微信机器人Webhook"
	EMAIL_SENDER = "你的发送邮箱@qq.com"
	EMAIL_PASSWORD = "你的邮箱授权码"
	EMAIL_RECEIVER = "你的接收邮箱@qq.com"
	# 交易配置
	MAX_SINGLE_POSITION = 0.2
	MAX_TOTAL_POSITION = 0.9
	DRAWDOWN_WARNING = 0.03
	DRAWDOWN_CRITICAL = 0.05

第二步:行情模块实现,封装通用行情接口
python

	# market_data.py 行情模块
	import tushare as ts
	import pandas as pd
	from datetime import datetime, timedelta
	from dotenv import load_dotenv
	import os
	
	load_dotenv()
	ts.set_token(os.getenv("TUSHARE_TOKEN"))
	pro = ts.pro_api()
	
	class MarketData:
	def __init__(self):
	self.today = datetime.now().strftime("%Y%m%d")
	self.trade_cal = self._get_trade_cal()
	
	def _get_trade_cal(self) -> pd.DataFrame:
	"""获取最近1年的交易日历,用来判断是不是交易日"""
	end_date = self.today
	start_date = (datetime.now() - timedelta(days=365)).strftime("%Y%m%d")
	df = pro.trade_cal(exchange='SSE', start_date=start_date, end_date=end_date, is_open='1')
	return df['cal_date'].tolist()
	
	def is_trade_day(self, date: str = None) -> bool:
	"""判断是不是交易日"""
	if date is None:
	date = self.today
	return date in self.trade_cal
	
	def get_daily_price(self, ts_code: str, start_date: str, end_date: str = None) -> pd.DataFrame:
	"""获取股票日K行情"""
	if end_date is None:
	end_date = self.today
	df = pro.daily(ts_code=ts_code, start_date=start_date, end_date=end_date)
	df = df.sort_values('trade_date').reset_index(drop=True)
	return df
	
	def get_latest_price(self, ts_code: str) -> float:
	"""获取股票最新实时价,用新浪免费接口"""
	import requests
	code = ts_code.split('.')[0]
	market = 'sh' if ts_code.endswith('.SH') else 'sz'
	url = f"https://hq.sinajs.cn/list={market}{code}"
	resp = requests.get(url, headers={"User-Agent": "Mozilla/5.0"}).text
	data = resp.split('"')[1].split(',')
	return float(data[3])
	
	def get_stock_list(self) -> pd.DataFrame:
	"""获取全部A股股票列表,过滤ST、退市股"""
	df = pro.stock_basic(exchange='', list_status='L', fields='ts_code,symbol,name,industry,list_date')
	# 过滤ST、*ST股
	df = df[~df['name'].str.contains('ST')]
	# 过滤上市不足1年的新股
	df['list_date'] = pd.to_datetime(df['list_date'])
	df = df[df['list_date'] < datetime.now() - timedelta(days=365)]
	return df.reset_index(drop=True)

讲解:用Tushare获取历史行情,新浪接口获取实时价,自动过滤ST和新股,避免踩雷。交易日历判断是不是交易日,非交易日自动不运行策略。

第三步:选股模块实现,这里用双因子选股示例
python

	# strategy.py 选股模块
	import pandas as pd
	import numpy as np
	from datetime import datetime, timedelta
	from market_data import MarketData
	
	class DoubleFactorStrategy:
	def __init__(self, market_data: MarketData, select_count: int = 5):
	self.market_data = market_data
	self.select_count = select_count # 每次选5只股票
	self.stock_list = market_data.get_stock_list()
	self.today = datetime.now().strftime("%Y%m%d")
	self.his_start_date = (datetime.now() - timedelta(days=60)).strftime("%Y%m%d")
	
	def _calculate_factor(self, ts_code: str) -> dict:
	"""计算单只股票的因子值:近20日涨幅+近20日波动率"""
	df = self.market_data.get_daily_price(ts_code, start_date=self.his_start_date)
	if len(df) < 20:
	return None
	# 计算近20日涨幅
	df['return_20'] = df['close'].pct_change(20)
	# 计算近20日波动率(标准差)
	df['volatility_20'] = df['pct_chg'].rolling(20).std()
	latest = df.iloc[-1]
	return {
	"ts_code": ts_code,
	"return_20": latest['return_20'],
	"volatility_20": latest['volatility_20'],
	"close": latest['close']
	}
	
	def generate_signals(self, trade_date: str = None) -> list:
	"""生成交易信号,输出次日交易清单"""
	if trade_date is None:
	# 默认生成下一个交易日的信号
	trade_date = self.today
	if not self.market_data.is_trade_day(trade_date):
	# 今天不是交易日,找下一个交易日
	for i in range(1, 10):
	next_date = (datetime.now() + timedelta(days=i)).strftime("%Y%m%d")
	if self.market_data.is_trade_day(next_date):
	trade_date = next_date
	break
	print(f"开始选股,目标交易日:{trade_date}")
	# 计算所有股票的因子值
	factor_list = []
	for idx, row in self.stock_list.iterrows():
	if idx % 100 == 0:
	print(f"已计算{idx}/{len(self.stock_list)}只股票")
	factor = self._calculate_factor(row['ts_code'])
	if factor:
	factor_list.append(factor)
	factor_df = pd.DataFrame(factor_list)
	# 因子标准化:涨幅越大越好,波动率越小越好
	factor_df['return_rank'] = factor_df['return_20'].rank(ascending=True)
	factor_df['volatility_rank'] = factor_df['volatility_20'].rank(ascending=False)
	factor_df['total_score'] = factor_df['return_rank'] + factor_df['volatility_rank']
	# 选得分最高的select_count只股票
	selected = factor_df.sort_values('total_score', ascending=False).head(self.select_count)
	# 生成交易信号,等权分配仓位
	position_per_stock = min(float(os.getenv("MAX_SINGLE_POSITION")), 
	float(os.getenv("MAX_TOTAL_POSITION")) / len(selected))
	signals = []
	for _, row in selected.iterrows():
	signals.append({
	"ts_code": row['ts_code'],
	"trade_date": trade_date,
	"direction": "buy",
	"target_position_ratio": position_per_stock,
	"reason": f"双因子得分{row['total_score']:.0f},涨幅排名{row['return_rank']:.0f},波动率排名{row['volatility_rank']:.0f}"
	})
	# 保存信号到文件
	import json
	with open("./trade_signals.json", 'w', encoding='utf-8') as f:
	json.dump(signals, f, ensure_ascii=False, indent=2)
	print(f"选股完成,共选中{len(signals)}只股票,信号已保存到trade_signals.json")
	return signals

讲解:这里用的是“低波+动量”双因子策略,选近20天涨得多、波动小的股票,你可以把_calculate_factor方法换成自己的选股逻辑,比如多因子、均线、MACD都可以,上层代码不用改。

第四步:整合所有模块,实现完整自动交易系统
python

	# main.py 主程序
	import os
	import time
	import schedule
	from datetime import datetime
	from dotenv import load_dotenv
	# 导入之前写的所有模块
	from market_data import MarketData
	from strategy import DoubleFactorStrategy
	from 模拟交易接口封装 import THSMockTrader
	from 自动化交易逻辑 import AutoTrader
	from 交易日志与监控 import init_logger, TradeLogger, AlertNotifier, TradeMonitor, log_api_request
	
	load_dotenv()
	
	def init_system():
	"""初始化所有系统组件"""
	# 1. 初始化日志
	logger = init_logger(log_level="INFO", log_dir="./trade_logs")
	trade_logger = TradeLogger(logger)
	print("日志模块初始化完成")
	
	# 2. 初始化报警
	alert_config = {
	"wechat_webhook": os.getenv("WECHAT_WEBHOOK"),
	"email_config": {
	"smtp_server": "smtp.qq.com",
	"smtp_port": 465,
	"sender": os.getenv("EMAIL_SENDER"),
	"password": os.getenv("EMAIL_PASSWORD"),
	"receiver": os.getenv("EMAIL_RECEIVER")
	}
	}
	notifier = AlertNotifier(alert_config)
	print("报警模块初始化完成")
	
	# 3. 初始化监控
	monitor_config = {
	"max_total_position": float(os.getenv("MAX_TOTAL_POSITION")),
	"max_single_position": float(os.getenv("MAX_SINGLE_POSITION")),
	"drawdown_warning": float(os.getenv("DRAWDOWN_WARNING")),
	"drawdown_critical": float(os.getenv("DRAWDOWN_CRITICAL"))
	}
	monitor = TradeMonitor(notifier, logger, monitor_config)
	print("监控模块初始化完成")
	
	# 4. 初始化行情模块
	market_data = MarketData()
	print("行情模块初始化完成")
	
	# 5. 初始化交易客户端
	for name in dir(THSMockTrader):
	if callable(getattr(THSMockTrader, name)) and not name.startswith('_'):
	setattr(THSMockTrader, name, log_api_request(getattr(THSMockTrader, name)))
	trader = THSMockTrader(
	cookie=os.getenv("THS_COOKIE"),
	account_id=os.getenv("THS_ACCOUNT_ID")
	)
	print("交易接口初始化完成")
	
	# 6. 初始化自动交易引擎
	auto_trader = AutoTrader(
	trader=trader,
	max_single_position=float(os.getenv("MAX_SINGLE_POSITION")),
	total_position_limit=float(os.getenv("MAX_TOTAL_POSITION"))
	)
	print("自动交易引擎初始化完成")
	
	return {
	"logger": logger,
	"trade_logger": trade_logger,
	"notifier": notifier,
	"monitor": monitor,
	"market_data": market_data,
	"trader": trader,
	"auto_trader": auto_trader
	}
	
	def run_stock_selection(system_components):
	"""盘后选股任务,交易日15:30运行"""
	market_data = system_components['market_data']
	if not market_data.is_trade_day():
	print("今日非交易日,不执行选股")
	return
	print("开始执行盘后选股...")
	strategy = DoubleFactorStrategy(market_data)
	signals = strategy.generate_signals()
	system_components['notifier'].send_alert(f"盘后选股完成,选中{len(signals)}只股票,明日自动交易")
	
	def run_trading(system_components):
	"""交易任务,交易日9:30运行"""
	market_data = system_components['market_data']
	if not market_data.is_trade_day():
	print("今日非交易日,不执行交易")
	return
	auto_trader = system_components['auto_trader']
	trade_logger = system_components['trade_logger']
	notifier = system_components['notifier']
	print("开始执行自动交易...")
	# 加载信号
	signals = auto_trader.load_trade_signals()
	if not signals:
	notifier.send_alert("今日无交易信号,不执行交易")
	return
	# 记录信号
	for signal in signals:
	trade_logger.log_signal(signal.__dict__)
	# 执行交易
	executed_orders = auto_trader.execute_trades(signals)
	# 记录订单
	for order in executed_orders:
	if order.status == 'deal':
	trade_logger.log_order_deal(order)
	# 发送交易结果通知
	notifier.send_alert(f"今日交易完成,共成交{len(executed_orders)}笔订单")
	
	def run_reconciliation(system_components):
	"""对账任务,交易日15:10运行"""
	market_data = system_components['market_data']
	if not market_data.is_trade_day():
	print("今日非交易日,不执行对账")
	return
	auto_trader = system_components['auto_trader']
	trade_logger = system_components['trade_logger']
	notifier = system_components['notifier']
	trader = system_components['trader']
	print("开始执行收盘对账...")
	signals = auto_trader.load_trade_signals()
	result = auto_trader.daily_reconciliation(signals)
	if result:
	notifier.send_alert("收盘对账通过,持仓与目标一致")
	else:
	notifier.send_alert("收盘对账失败,请立即检查持仓", level="critical")
	# 记录每日报告
	account = trader.get_account_info()
	positions = trader.get_positions()
	# 读取前一日总资产计算收益
	import json
	import glob
	log_files = sorted(glob.glob("./trade_logs/*_trade.log"))
	daily_return = 0.0
	if len(log_files) >= 2:
	# 读取前一天的日志获取总资产
	with open(log_files[-2], 'r', encoding='utf-8') as f:
	for line in f:
	log = json.loads(line)
	if log.get('type') == 'daily_report':
	last_total_asset = log['total_asset']
	daily_return = (account.total_asset - last_total_asset) / last_total_asset
	break
	trade_logger.log_daily_report(account, positions, daily_return)
	notifier.send_alert(f"每日报告:总资产{account.total_asset:.2f},当日收益{daily_return*100:.2f}%,仓位{account.position_ratio*100:.1f}%")
	
	if __name__ == '__main__':
	print("正在启动自动交易系统...")
	system_components = init_system()
	monitor = system_components['monitor']
	trader = system_components['trader']
	
	# 配置定时任务
	# 交易日15:30选股
	schedule.every().day.at("15:30").do(run_stock_selection, system_components=system_components)
	# 交易日9:30交易
	schedule.every().day.at("09:30").do(run_trading, system_components=system_components)
	# 交易日15:10对账
	schedule.every().day.at("15:10").do(run_reconciliation, system_components=system_components)
	# 每分钟运行一次监控
	schedule.every(1).minutes.do(monitor.run_monitor, trader=trader)
	
	print("自动交易系统启动成功,等待任务执行...")
	# 发送启动通知
	system_components['notifier'].send_alert("自动交易系统启动成功,开始运行")
	
	# 保持程序运行
	while True:
	schedule.run_pending()
	time.sleep(1)

逐行讲解:
所有配置都存在.env文件里,不用改代码就能换接口、换参数,避免敏感信息泄露。
定时任务自动判断交易日,非交易日自动跳过,不用手动启停。
模块完全解耦,要换选股策略只要改strategy.py,要换交易接口只要换THSMockTrader为券商实盘接口,其他代码都不用动。
全流程自动通知,选股完成、交易完成、对账结果、每日收益都会发微信通知,不用天天盯着系统。

29.5.4 部署与运行指南
1.本地测试:
先把THS_COOKIE和THS_ACCOUNT_ID换成你自己的,运行main.py,系统会自动启动,你可以手动调用run_stock_selection和run_trading测试选股和下单逻辑是否正常。
2.服务器部署:
建议买个云服务器(阿里云/腾讯云的轻量应用服务器就行,每年100多块钱),把代码上传到服务器,用nohup后台运行:
bash
3.
nohup python main.py > run.log 2>&1 &
4.这样就算你关了电脑,系统也会自动运行,每天自动选股、交易、对账。
5.模拟盘测试要求:
最少跑3个月模拟盘,满足三个条件再上实盘:
模拟收益和回测收益偏差小于10%
无漏单、错单、对账异常
最大回撤符合预期,没有超过策略最大回撤限制

29.5.5 基础知识拓展:实盘部署的注意事项

  1. 安全配置
    所有敏感信息不要硬编码在代码里,存在环境变量或者配置文件里,配置文件不要上传到GitHub。
    服务器开放端口只开必要的,不要把交易接口暴露到公网,避免被攻击。
    定期更换Cookie、接口密码,避免泄露。
  2. 稳定性保障
    加守护进程:用supervisor监控程序运行状态,程序崩溃自动重启。
    定期备份:日志、交易信号、持仓数据每天备份到云存储,避免服务器故障丢失数据。
    测试环境和生产环境分离:修改代码先在测试环境跑通,再部署到生产环境,不要在交易时间改代码。
  3. 常见故障处理
故障现象 原因 处理方法
程序启动失败 依赖没装、配置文件错误 查看run.log错误信息,安装对应依赖、修正配置
选股一直跑不完 网络慢、Tushare接口限流 增加选股超时时间,或者换更快的行情数据源
下单失败 Cookie失效、接口限流 重新抓同花顺Cookie,加大请求间隔
报警收不到 Webhook地址错误、邮箱配置错误 测试报警接口,确认配置正确

29.5.6 总结:自动交易系统的核心原则
1.极简原则:系统越简单越稳定,不要加太多花里胡哨的功能,核心逻辑能跑通就行,功能越多出问题的概率越大。
2.可复现原则:所有交易都有日志,所有信号都能回溯,出问题能100%复现当时的场景,找到原因。
3.安全优先原则:风控、日志、监控是系统的核心,交易逻辑反而最简单,不要为了追求收益忽略风控。
4.先模拟后实盘原则:不管你对自己的策略多有信心,都要先跑3个月模拟盘,模拟盘能稳定盈利,实盘才有可能盈利。
到这里,整个模拟交易章节就全部结束了。你现在已经掌握了从接口对接、自动交易、日志监控到完整系统搭建的全部技能,接下来我们就进入实盘对接环节,把这套系统真正对接券商实盘接口。
下一章咱们就讲券商实盘交易接口对接:华泰、中信、东方财富API对接实现。

本站原创,转载请注明出处:https://www.xin3721.com/ArticlePrograme/csharp49738.html


相关教程