#coding:gbk
import pandas as pd
import numpy as np
import time
import copy
from datetime import datetime as dts
import os
import logging
from datetime import timedelta
import json
# 配置日志记录 - 同时输出到控制台和文件
logging.basicConfig(
level=logging.INFO,
format='%(asctime)s - %(levelname)s - %(message)s',
handlers=[
logging.FileHandler("grid_strategy.log", encoding="utf-8"),
logging.StreamHandler()
]
)
class G():
pass
g = G()
g.acct = 'netzy40160975'
g.acct_type = 'STOCK'
g.k_line_period = '1d' # 选股K线周期改为日线
g.buy_code = 23 if g.acct_type == 'STOCK' else 33
g.sell_code = 24 if g.acct_type == 'STOCK' else 34
g.buy_remark = '开仓'
g.sell_remark = '平仓'
g.grid_params_file = r'D:\实盘网格\网格参数.CSV' # 网格参数文件路径
g.trade_records = {} # 记录每只证券当日交易次数
g.pending_orders = {} # 记录当前挂单
g.max_trades_per_day = 3 # 每只证券每日最大交易次数
g.TRADE_START = "092500"
g.TRADE_END = "235700"
# 策略运行统计
g.strategy_stats = {
"start_time": None,
"total_checks": 0,
"total_buy_orders": 0,
"total_sell_orders": 0,
"executed_buy_orders": 0,
"executed_sell_orders": 0,
"current_positions": 0,
"total_profit": 0.0
}
# 检查文件是否存在,不存在则创建示例文件
def create_sample_grid_file():
"""创建示例网格参数文件"""
if not os.path.exists(g.grid_params_file):
logging.info(f"网格参数文件不存在,创建示例文件: {g.grid_params_file}")
sample_data = {
'证券代码': ['600028.SH'],
'基准价(元)': [10.0],
'买入网格大小(元)': [0.5],
'卖出网格大小(元)': [0.5],
'买入网格调整因子(元)': [0.1],
'卖出网格调整因子(元)': [0.1],
'买入股票数量(股)': [200],
'卖出股票数量(股)': [200],
'买入增加步长(股)': [100],
'卖出增加步长(股)': [100],
'昨夜拥股数量(股)': [0],
'持仓数量(股)': [0],
'可用数量(股)': [0],
'最低仓位红线(股)': [100],
'较低仓位阈值(股)': [500],
'较高仓位阈值(股)': [1500],
'最高仓位红线(股)': [2000],
'被动网格收益倍数': [5],
'在途数量(股)': [0],
'冻结数量(股)': [0],
'最新价(元)': [0],
'成交时间': [''],
'成交数量': [0]
}
df = pd.DataFrame(sample_data)
df.to_csv(g.grid_params_file, index=False, encoding='gbk')
logging.info("示例网格参数文件创建完成")
# 读取网格参数
def load_grid_parameters():
"""读取并解析网格参数文件"""
create_sample_grid_file() # 确保文件存在
try:
df = pd.read_csv(g.grid_params_file, encoding='gbk')
logging.info(f"成功加载网格参数,共{len(df)}只证券")
return df
except Exception as e:
logging.error(f"加载网格参数失败: {e}")
return pd.DataFrame()
# 保存网格参数
def save_grid_parameters(df):
"""保存网格参数到文件"""
try:
df.to_csv(g.grid_params_file, index=False, encoding='gbk')
logging.info(f"已保存网格参数,共{len(df)}只证券")
except Exception as e:
logging.error(f"保存网格参数失败: {e}")
# 获取账户持仓信息
def get_account_positions(C):
"""获取账户持仓信息"""
logging.info("开始查询账户持仓信息...")
start_time = time.time()
positions = []
try:
pos_data = get_trade_detail_data(g.acct, g.acct_type, 'position')
for dt in pos_data:
code = f"{dt.m_strInstrumentID}.{dt.m_strExchangeID}"
positions.append({
'code': code,
'name': dt.m_strInstrumentName,
'volume': dt.m_nVolume,
'can_use_volume': dt.m_nCanUseVolume
})
end_time = time.time()
logging.info(f"持仓查询完成: {len(positions)}只证券, 耗时{end_time - start_time:.2f}秒")
return positions
except Exception as e:
logging.error(f"查询持仓信息失败: {e}")
return positions
# 更新网格参数中的持仓信息
def update_position_info(df, positions):
"""根据持仓信息更新网格参数"""
updated = False
pos_dict = {p['code']: p for p in positions}
for index, row in df.iterrows():
code = row['证券代码']
if code in pos_dict:
pos = pos_dict[code]
if row['持仓数量(股)'] != pos['volume'] or row['可用数量(股)'] != pos['can_use_volume']:
df.at[index, '昨夜拥股数量(股)'] = pos['volume']
df.at[index, '持仓数量(股)'] = pos['volume']
df.at[index, '可用数量(股)'] = pos['can_use_volume']
updated = True
if updated:
logging.info("已更新网格参数中的持仓信息")
return df, updated
# 判断是否为可交易品种
def is_tradable(code, row):
"""判断证券是否可交易"""
pure_code = code.split('.')[0]
# 判断是否为可转债或基金(当日可交易)
if pure_code.startswith(('11', '12', '5', '6', '15')):
return True
# 判断是否为股票且有昨夜持仓
if pure_code.startswith(('60', '00', '001')) and row['昨夜拥股数量(股)'] > 0:
return True
return False
# 计算调整后的网格大小
def calculate_adjusted_grid(row, position):
"""根据仓位计算调整后的网格大小"""
volume = position['volume']
lower_threshold = row['较低仓位阈值(股)']
upper_threshold = row['较高仓位阈值(股)']
buy_grid = row['买入网格大小(元)']
sell_grid = row['卖出网格大小(元)']
buy_factor = row['买入网格调整因子(元)']
sell_factor = row['卖出网格调整因子(元)']
# 计算买入网格调整
if volume < lower_threshold:
buy_adjust = buy_grid * 0.2
logging.debug(f"{row['证券代码']} 仓位低于较低阈值,买入网格增加{buy_adjust}元")
elif volume > upper_threshold:
buy_adjust = buy_grid * 0.8
logging.debug(f"{row['证券代码']} 仓位高于较高阈值,买入网格增加{buy_adjust}元")
else:
buy_adjust = buy_grid * 0.5
logging.debug(f"{row['证券代码']} 仓位正常,买入网格增加{buy_adjust}元")
# 计算卖出网格调整
if volume < lower_threshold:
sell_adjust = sell_grid * 0.8
logging.debug(f"{row['证券代码']} 仓位低于较低阈值,卖出网格增加{sell_adjust}元")
elif volume > upper_threshold:
sell_adjust = sell_grid * 0.2
logging.debug(f"{row['证券代码']} 仓位高于较高阈值,卖出网格增加{sell_adjust}元")
else:
sell_adjust = sell_grid * 0.5
logging.debug(f"{row['证券代码']} 仓位正常,卖出网格增加{sell_adjust}元")
adjusted_buy_grid = buy_grid + buy_adjust
adjusted_sell_grid = sell_grid + sell_adjust
return adjusted_buy_grid, adjusted_sell_grid
# 计算交易数量
def calculate_trade_volume(row, position, is_buy, trade_count):
"""计算交易数量"""
base_volume = row['买入股票数量(股)'] if is_buy else row['卖出股票数量(股)']
step = row['买入增加步长(股)'] if is_buy else row['卖出增加步长(股)']
# 根据连续交易次数增加交易量
adjusted_volume = base_volume + step * trade_count
# 确保交易量不小于基准量
adjusted_volume = max(adjusted_volume, base_volume)
logging.debug(f"{row['证券代码']} {'买入' if is_buy else '卖出'}数量计算: "
f"基准量={base_volume}, 连续交易次数={trade_count}, 调整后={adjusted_volume}")
return adjusted_volume
# 检查并处理挂单
def handle_orders(C, df, positions):
"""检查市场情况并处理挂单"""
pos_dict = {p['code']: p for p in positions}
today = dts.today().strftime("%Y-%m-%d")
for index, row in df.iterrows():
code = row['证券代码']
base_price = row['基准价(元)']
latest_price = row['最新价(元)']
# 初始化当日交易次数
if code not in g.trade_records:
g.trade_records[code] = {
'date': today,
'buy_count': 0,
'sell_count': 0
}
# 检查是否超过每日交易次数限制
if g.trade_records[code]['buy_count'] + g.trade_records[code]['sell_count'] >= g.max_trades_per_day:
logging.info(f"{code} 今日交易次数已达上限({g.max_trades_per_day}),跳过")
continue
# 检查是否有持仓信息
if code not in pos_dict:
logging.warning(f"{code} 无持仓信息,跳过")
continue
position = pos_dict[code]
# 判断是否可交易
if not is_tradable(code, row):
logging.info(f"{code} 不可交易(非可转债/基金或无昨夜持仓),跳过")
continue
# 计算调整后的网格大小
adjusted_buy_grid, adjusted_sell_grid = calculate_adjusted_grid(row, position)
# 计算买卖价格
buy_price = base_price - adjusted_buy_grid
sell_price = base_price + adjusted_sell_grid
# 计算交易数量
buy_volume = calculate_trade_volume(row, position, True, g.trade_records[code]['buy_count'])
sell_volume = calculate_trade_volume(row, position, False, g.trade_records[code]['sell_count'])
# 检查是否需要挂买入单
if latest_price <= buy_price and position['can_use_volume'] >= 0:
# 检查是否已挂买入单
if code in g.pending_orders and 'buy' in g.pending_orders[code]:
logging.info(f"{code} 已挂买入单,跳过重复挂单")
else:
# 执行买入挂单
try:
# 确保买入数量为正且不超过可用资金
if buy_volume > 0:
# 下单并记录挂单
order_id = passorder(g.buy_code, 1101, g.acct, code, 11, buy_price, buy_volume,
'网格买入', 2, '网格策略买入', C)
if order_id:
g.pending_orders.setdefault(code, {})['buy'] = order_id
g.strategy_stats["total_buy_orders"] += 1
logging.info(f"挂入买入单: {code}, 价格={buy_price}, 数量={buy_volume}")
else:
logging.error(f"挂入买入单失败: {code}")
else:
logging.warning(f"{code} 买入数量为0,跳过")
except Exception as e:
logging.error(f"挂入买入单异常: {code}, 错误: {e}")
# 检查是否需要挂卖出单
if latest_price >= sell_price and position['can_use_volume'] > 0:
# 检查是否已挂卖出单
if code in g.pending_orders and 'sell' in g.pending_orders[code]:
logging.info(f"{code} 已挂卖出单,跳过重复挂单")
else:
# 执行卖出挂单
try:
# 确保卖出数量为正且不超过可用数量
if sell_volume > 0 and sell_volume <= position['can_use_volume']:
# 下单并记录挂单
order_id = passorder(g.sell_code, 1101, g.acct, code, 11, sell_price, sell_volume,
'网格卖出', 2, '网格策略卖出', C)
if order_id:
g.pending_orders.setdefault(code, {})['sell'] = order_id
g.strategy_stats["total_sell_orders"] += 1
logging.info(f"挂入卖出单: {code}, 价格={sell_price}, 数量={sell_volume}")
else:
logging.error(f"挂入卖出单失败: {code}")
else:
logging.warning(f"{code} 卖出数量无效或超过可用数量,跳过")
except Exception as e:
logging.error(f"挂入卖出单异常: {code}, 错误: {e}")
# 检查订单状态并更新
update_order_status(C, code, position, row, latest_price)
# 更新订单状态
def update_order_status(C, code, position, row, latest_price):
"""更新订单状态并处理成交"""
if code not in g.pending_orders:
return
pending_orders = g.pending_orders[code].copy()
for order_type, order_id in pending_orders.items():
try:
# 查询订单状态
order_status = get_order_status(C, order_id)
if order_status == 'filled': # 已成交
# 处理成交
if order_type == 'buy':
handle_buy_execution(C, code, position, row, latest_price, order_id)
else:
handle_sell_execution(C, code, position, row, latest_price, order_id)
# 移除已成交订单
del g.pending_orders[code][order_type]
if not g.pending_orders[code]:
del g.pending_orders[code]
elif order_status == 'cancelled': # 已取消
# 移除已取消订单
del g.pending_orders[code][order_type]
if not g.pending_orders[code]:
del g.pending_orders[code]
logging.info(f"订单已取消: {code}, 类型: {order_type}, 订单ID: {order_id}")
except Exception as e:
logging.error(f"查询订单状态异常: {code}, 订单ID: {order_id}, 错误: {e}")
# 处理买入成交
def handle_buy_execution(C, code, position, row, latest_price, order_id):
"""处理买入成交"""
# 获取订单详情
order_detail = get_order_detail(C, order_id)
if not order_detail:
logging.error(f"获取买入订单详情失败: {code}, 订单ID: {order_id}")
return
# 更新网格参数
df = load_grid_parameters()
for index, r in df.iterrows():
if r['证券代码'] == code:
# 更新基准价为成交价
df.at[index, '基准价(元)'] = order_detail['price']
df.at[index, '最新价(元)'] = order_detail['price']
df.at[index, '成交时间'] = dts.now().strftime("%Y-%m-%d %H:%M:%S")
df.at[index, '成交数量'] = order_detail['volume']
# 更新交易记录
g.trade_records[code]['buy_count'] += 1
g.strategy_stats["executed_buy_orders"] += 1
# 计算盈亏
profit = (latest_price - order_detail['price']) * order_detail['volume']
g.strategy_stats["total_profit"] += profit
logging.info(f"买入成交: {code}, 价格={order_detail['price']}, 数量={order_detail['volume']}, "
f"盈亏={profit:.2f}元")
break
save_grid_parameters(df)
# 处理卖出成交
def handle_sell_execution(C, code, position, row, latest_price, order_id):
"""处理卖出成交"""
# 获取订单详情
order_detail = get_order_detail(C, order_id)
if not order_detail:
logging.error(f"获取卖出订单详情失败: {code}, 订单ID: {order_id}")
return
# 更新网格参数
df = load_grid_parameters()
for index, r in df.iterrows():
if r['证券代码'] == code:
# 更新基准价为成交价
df.at[index, '基准价(元)'] = order_detail['price']
df.at[index, '最新价(元)'] = order_detail['price']
df.at[index, '成交时间'] = dts.now().strftime("%Y-%m-%d %H:%M:%S")
df.at[index, '成交数量'] = order_detail['volume']
# 更新交易记录
g.trade_records[code]['sell_count'] += 1
g.strategy_stats["executed_sell_orders"] += 1
# 计算盈亏
profit = (order_detail['price'] - row['基准价(元)']) * order_detail['volume']
g.strategy_stats["total_profit"] += profit
logging.info(f"卖出成交: {code}, 价格={order_detail['price']}, 数量={order_detail['volume']}, "
f"盈亏={profit:.2f}元")
break
save_grid_parameters(df)
# 获取证券行情数据
def get_stock_quotes(C, codes, max_retries=3):
"""获取证券行情数据"""
if not codes:
return {}
logging.info(f"开始获取{len(codes)}只证券的行情数据: {codes}")
if not is_trading_time():
logging.info("非交易时段,跳过行情获取")
return {}
for i in range(max_retries):
try:
start_time = time.time()
result = C.get_full_tick(codes)
end_time = time.time()
# 使用实时价格作为当日收盘价
for code in result:
if code in result and 'lastPrice' in result[code] and result[code]['lastPrice'] > 0:
result[code]['close'] = result[code]['lastPrice']
else:
logging.warning(f"{code} 无最新价,使用前收价")
result[code]['close'] = result[code].get('preClosePrice', 0)
missing_codes = [code for code in codes if code not in result]
if missing_codes:
logging.warning(f"第{i+1}次尝试,缺少{len(missing_codes)}只证券的行情: {missing_codes}")
else:
logging.info(f"成功获取{len(codes)}只证券的行情数据,耗时{end_time - start_time:.2f}秒")
return result
time.sleep(0.5)
except Exception as e:
logging.warning(f"第{i+1}次重试获取行情失败: {e}")
logging.error(f"达到最大重试次数,未能获取{codes}的行情数据")
return {}
# 更新网格参数中的最新价格
def update_latest_prices(df, C):
"""更新网格参数中的最新价格"""
codes = df['证券代码'].tolist()
quotes = get_stock_quotes(C, codes)
updated = False
for index, row in df.iterrows():
code = row['证券代码']
if code in quotes:
latest_price = quotes[code].get('lastPrice', quotes[code].get('close', 0))
if latest_price != row['最新价(元)']:
df.at[index, '最新价(元)'] = latest_price
updated = True
if updated:
logging.info("已更新网格参数中的最新价格")
return df, updated
# 检查是否在交易时间内
def is_trading_time():
"""检查当前是否在交易时间内"""
now_time = str(dts.now())[11:19].replace(':', '')
is_trading = g.TRADE_START <= now_time <= g.TRADE_END
logging.debug(f"当前时间{now_time},是否交易时段: {is_trading}")
return is_trading
# 打印策略统计信息
def print_strategy_statistics():
"""打印策略运行统计信息"""
logging.info("=" * 50)
logging.info("策略运行统计")
logging.info("=" * 50)
logging.info(f"策略启动时间: {g.strategy_stats['start_time']}")
logging.info(f"总检查次数: {g.strategy_stats['total_checks']}")
logging.info(f"总挂单: 买入={g.strategy_stats['total_buy_orders']}, 卖出={g.strategy_stats['total_sell_orders']}")
logging.info(f"成交订单: 买入={g.strategy_stats['executed_buy_orders']}, 卖出={g.strategy_stats['executed_sell_orders']}")
logging.info(f"当前持仓: {g.strategy_stats['current_positions']}只")
logging.info(f"总盈亏: {g.strategy_stats['total_profit']:.2f}元")
logging.info("=" * 50)
# 初始化策略
def init(C):
"""策略初始化函数"""
if not is_trading_time():
return
logging.info('开始运行网格交易策略')
# 记录策略启动时间
g.strategy_stats["start_time"] = dts.now().strftime("%Y-%m-%d %H:%M:%S")
# 加载网格参数
grid_params = load_grid_parameters()
if grid_params.empty:
logging.error("网格参数文件为空,策略退出")
return
logging.info(f"成功加载{len(grid_params)}只证券的网格参数")
# 主循环
loop_count = 0
while True:
loop_count += 1
current_time = dts.now().strftime("%Y-%m-%d %H:%M:%S")
logging.info(f"=" * 30)
logging.info(f"主循环 #{loop_count} - {current_time}")
logging.info(f"=" * 30)
# 检查是否在交易时间内
if is_trading_time():
g.strategy_stats["total_checks"] += 1
# 获取账户持仓
positions = get_account_positions(C)
g.strategy_stats["current_positions"] = len(positions)
# 更新网格参数中的持仓信息
grid_params, pos_updated = update_position_info(grid_params, positions)
# 更新网格参数中的最新价格
grid_params, price_updated = update_latest_prices(grid_params, C)
# 如果有更新,保存网格参数
if pos_updated or price_updated:
save_grid_parameters(grid_params)
# 处理挂单
handle_orders(C, grid_params, positions)
# 每5次循环打印一次策略统计
if loop_count % 5 == 0:
print_strategy_statistics()
else:
logging.info("当前非交易时段,等待中...")
# 每10秒运行一次
time.sleep(10)
# 主函数
if __name__ == "__main__":
# 此处需要根据实际情况初始化交易接口C
# 示例: C = TradingInterface()
C = None # 实际使用时替换为真实交易接口实例
init(C) 这段代码运行提示:
【2025-07-04 15:08:34.797】 0D:\东北证券NET专业版\python\智能网格.py_000300123: 策略停止
【2025-07-04 15:08:39.301】 0D:\东北证券NET专业版\python\智能网格.py_000300126SyntaxError:invalid syntax (<string>, line 152)
【2025-07-04 15:08:39.302】 0D:\东北证券NET专业版\python\智能网格.py_000300126SyntaxError:invalid syntax (<string>, line 152)
【2025-07-04 15:14:02.901】 0D:\东北证券NET专业版\python\智能网格.py_000300126: 策略停止
【2025-07-04 15:14:04.020】 0D:\东北证券NET专业版\python\智能网格.py_000300129SyntaxError:invalid syntax (<string>, line 152)
【2025-07-04 15:14:04.021】 0D:\东北证券NET专业版\python\智能网格.py_000300129SyntaxError:invalid syntax (<string>, line 152)
【2025-07-04 15:16:24.716】 0D:\东北证券NET专业版\python\智能网格.py_000300129: 策略停止
【2025-07-04 15:16:26.910】 0D:\东北证券NET专业版\python\智能网格.py_000300132SyntaxError:invalid syntax (<string>, line 146)
【2025-07-04 15:16:26.910】 0D:\东北证券NET专业版\python\智能网格.py_000300132SyntaxError:invalid syntax (<string>, line 146)
【2025-07-04 15:25:26.031】 0D:\东北证券NET专业版\python\智能网格.py_000300132: 策略停止
【2025-07-04 15:25:31.694】 [trade]start trading mode
【2025-07-04 15:26:20.262】 0D:\东北证券NET专业版\python\智能网格.py_000300139: 策略停止
【2025-07-04 15:26:25.949】 2025-07-04 15:26:25,942 - INFO - 开始运行网格交易策略
【2025-07-04 15:26:25.949】 2025-07-04 15:26:25,945 - ERROR - 加载网格参数失败: Initializing from file failed
【2025-07-04 15:26:25.949】 2025-07-04 15:26:25,945 - ERROR - 网格参数文件为空,策略退出
【2025-07-04 15:26:25.949】 [trade]start trading mode 请检查修复
最新发布