2140 lines
100 KiB
Python
2140 lines
100 KiB
Python
'''
|
||
#公众号:松鼠Quant
|
||
#主页:www.quant789.com
|
||
#本策略仅作学习交流使用,实盘交易盈亏投资者个人负责!!!
|
||
#版权归松鼠Quant所有,禁止转发、转卖源码违者必究。
|
||
|
||
该代码的主要目的是处理Tick数据并生成交易信号。代码中定义了一个tickcome函数,它接收到Tick数据后会进行一系列的处理,包括构建Tick字典、更新上一个Tick的成交量、保存Tick数据、生成K线数据等。其中涉及到的一些函数有:
|
||
|
||
on_tick(tick): 处理单个Tick数据,根据Tick数据生成K线数据。
|
||
tickdata(df, symbol): 处理Tick数据,生成K线数据。
|
||
orderflow_df_new(df_tick, df_min, symbol): 处理Tick和K线数据,生成订单流数据。
|
||
GetOrderFlow_dj(kData): 计算订单流的信号指标。
|
||
除此之外,代码中还定义了一个MyTrader类,继承自TraderApiBase,用于实现交易相关的功能。
|
||
#公众号:松鼠Quant
|
||
#主页:www.quant789.com
|
||
#本策略仅作学习交流使用,实盘交易盈亏投资者个人负责!!!
|
||
#版权归松鼠Quant所有,禁止转发、转卖源码违者必究。
|
||
'''
|
||
from multiprocessing import Process, Queue
|
||
from AlgoPlus.CTP.MdApi import run_tick_engine
|
||
from AlgoPlus.CTP.FutureAccount import get_simulate_account
|
||
from AlgoPlus.CTP.FutureAccount import FutureAccount
|
||
from AlgoPlus.CTP.TraderApiBase import TraderApiBase
|
||
from AlgoPlus.ta.time_bar import tick_to_bar
|
||
import pandas as pd
|
||
from datetime import datetime
|
||
from datetime import time as s_time
|
||
import operator
|
||
import time
|
||
import numpy as np
|
||
import os
|
||
import re
|
||
import json
|
||
import requests # 添加requests库用于API调用
|
||
from openai import OpenAI # 导入OpenAI客户端
|
||
import threading
|
||
from queue import Queue as ThreadQueue
|
||
import warnings
|
||
warnings.filterwarnings("ignore", category=FutureWarning)
|
||
|
||
# 在文件顶部定义全局变量
|
||
tickdatadict = {} # 存储Tick数据的字典
|
||
quotedict = {} # 存储行情数据的字典
|
||
ofdatadict = {} # 存储K线数据的字典
|
||
trader_df = pd.DataFrame({}) # 存储交易数据的DataFrame对象
|
||
previous_volume = {} # 上一个Tick的成交量
|
||
tsymbollist={}
|
||
# 全局LLM配置变量
|
||
GLOBAL_LLM_CONFIG = {} # 全局大模型配置参数
|
||
# 全局交易信号队列,用于存储AI模型生成的交易信号
|
||
AI_SIGNAL_QUEUE = ThreadQueue()
|
||
# 标记是否有AI线程正在运行
|
||
AI_THREAD_RUNNING = False
|
||
# K线时间粒度
|
||
BAR_RESAMPLE_RULE = '1T' # 设置K线时间粒度,默认1分钟
|
||
|
||
|
||
def tickcome(md_queue):
|
||
global previous_volume
|
||
|
||
data=md_queue
|
||
instrument_id = data['InstrumentID'].decode() # 品种代码
|
||
ActionDay = data['ActionDay'].decode() # 交易日日期
|
||
update_time = data['UpdateTime'].decode() # 更新时间
|
||
#zzg_quant
|
||
update_millisec = str(data['UpdateMillisec']) # 更新毫秒数
|
||
created_at = ActionDay[:4] + '-' + ActionDay[4:6] + '-' + ActionDay[6:] + ' ' + update_time + '.' + update_millisec #创建时间
|
||
# 构建tick字典
|
||
tick = {
|
||
'symbol': instrument_id, # 品种代码和交易所ID
|
||
'created_at':datetime.strptime(created_at, "%Y-%m-%d %H:%M:%S.%f"),
|
||
#'created_at': created_at, # 创建时间
|
||
'price': float(data['LastPrice']), # 最新价
|
||
'last_volume': int(data['Volume']) - previous_volume.get(instrument_id, 0) if previous_volume.get(instrument_id, 0) != 0 else 0, # 瞬时成交量
|
||
'bid_p': float(data['BidPrice1']), # 买价
|
||
'bid_v': int(data['BidVolume1']), # 买量
|
||
'ask_p': float(data['AskPrice1']), # 卖价
|
||
'ask_v': int(data['AskVolume1']), # 卖量
|
||
'UpperLimitPrice': float(data['UpperLimitPrice']), # 涨停价
|
||
'LowerLimitPrice': float(data['LowerLimitPrice']), # 跌停价
|
||
'TradingDay': data['TradingDay'].decode(), # 交易日日期
|
||
'cum_volume': int(data['Volume']), # 最新总成交量
|
||
'cum_amount': float(data['Turnover']), # 最新总成交额
|
||
'cum_position': int(data['OpenInterest']), # 合约持仓量
|
||
}
|
||
# 更新上一个Tick的成交量
|
||
previous_volume[instrument_id] = int(data['Volume'])
|
||
if tick['last_volume']>0:
|
||
#print(tick['created_at'],'vol:',tick['last_volume'])
|
||
# 处理Tick数据
|
||
on_tick(tick)
|
||
|
||
|
||
def can_time(hour, minute):
|
||
hour = str(hour)
|
||
minute = str(minute)
|
||
if len(minute) == 1:
|
||
minute = "0" + minute
|
||
return int(hour + minute)
|
||
|
||
def on_tick(tick):
|
||
|
||
tm=can_time(tick['created_at'].hour,tick['created_at'].minute)
|
||
#print(tick['symbol'])
|
||
#print(1)
|
||
#if tm>1500 and tm<2100 :
|
||
# return
|
||
if tick['last_volume']==0:
|
||
return
|
||
quotes = tick
|
||
timetick=str(tick['created_at']).replace('+08:00', '')
|
||
tsymbol=tick['symbol']
|
||
if tsymbol not in tsymbollist.keys():
|
||
# 获取tick的买卖价和买卖量
|
||
#zzg_quant
|
||
tsymbollist[tsymbol]=tick
|
||
bid_p=quotes['bid_p']
|
||
ask_p=quotes['ask_p']
|
||
bid_v=quotes['bid_v']
|
||
ask_v=quotes['ask_v']
|
||
else:
|
||
# 获取上一个tick的买卖价和买卖量
|
||
rquotes =tsymbollist[tsymbol]
|
||
bid_p=rquotes['bid_p']
|
||
ask_p=rquotes['ask_p']
|
||
bid_v=rquotes['bid_v']
|
||
ask_v=rquotes['ask_v']
|
||
tsymbollist[tsymbol]=tick
|
||
tick_dt=pd.DataFrame({'datetime':timetick,'symbol':tick['symbol'],'mainsym':tick['symbol'].rstrip('0123456789').upper(),'lastprice':tick['price'],
|
||
'vol':tick['last_volume'],
|
||
'bid_p':bid_p,'ask_p':ask_p,'bid_v':bid_v,'ask_v':ask_v},index=[0])
|
||
sym = tick_dt['symbol'][0]
|
||
#print(tick_dt)
|
||
tickdata(tick_dt,sym)
|
||
|
||
#公众号:松鼠Quant
|
||
#主页:www.quant789.com
|
||
#本策略仅作学习交流使用,实盘交易盈亏投资者个人负责!!!
|
||
#版权归松鼠Quant所有,禁止转发、转卖源码违者必究。
|
||
|
||
def data_of(df):
|
||
global trader_df
|
||
# 将df数据合并到trader_df中
|
||
trader_df = pd.concat([trader_df, df], ignore_index=True)
|
||
#print('trader_df: ', len(trader_df))
|
||
#print(trader_df)
|
||
|
||
def process(bidDict, askDict, symbol):
|
||
try:
|
||
# 尝试从quotedict中获取对应品种的报价数据
|
||
dic = quotedict[symbol]
|
||
bidDictResult = dic['bidDictResult']
|
||
askDictResult = dic['askDictResult']
|
||
except:
|
||
# 如果获取失败,则初始化bidDictResult和askDictResult为空字典
|
||
bidDictResult, askDictResult = {}, {}
|
||
|
||
# 将所有买盘字典和卖盘字典的key合并,并按升序排序
|
||
sList = sorted(set(list(bidDict.keys()) + list(askDict.keys())))
|
||
|
||
# 遍历所有的key,将相同key的值进行累加
|
||
for s in sList:
|
||
if s in bidDict:
|
||
if s in bidDictResult:
|
||
bidDictResult[s] = int(bidDict[s]) + bidDictResult[s]
|
||
else:
|
||
bidDictResult[s] = int(bidDict[s])
|
||
if s not in askDictResult:
|
||
askDictResult[s] = 0
|
||
else:
|
||
if s in askDictResult:
|
||
askDictResult[s] = int(askDict[s]) + askDictResult[s]
|
||
else:
|
||
askDictResult[s] = int(askDict[s])
|
||
if s not in bidDictResult:
|
||
bidDictResult[s] = 0
|
||
|
||
# 构建包含bidDictResult和askDictResult的字典,并存入quotedict中
|
||
df = {'bidDictResult': bidDictResult, 'askDictResult': askDictResult}
|
||
quotedict[symbol] = df
|
||
|
||
return bidDictResult, askDictResult
|
||
#公众号:松鼠Quant
|
||
#主页:www.quant789.com
|
||
#本策略仅作学习交流使用,实盘交易盈亏投资者个人负责!!!
|
||
#版权归松鼠Quant所有,禁止转发、转卖源码违者必究。
|
||
|
||
|
||
def tickdata(df,symbol):
|
||
tickdata =pd.DataFrame({'datetime':df['datetime'],'symbol':df['symbol'],'lastprice':df['lastprice'],
|
||
'volume':df['vol'],'bid_p':df['bid_p'],'bid_v':df['bid_v'],'ask_p':df['ask_p'],'ask_v':df['ask_v']})
|
||
try:
|
||
if symbol in tickdatadict.keys():
|
||
rdf=tickdatadict[symbol]
|
||
rdftm=pd.to_datetime(rdf['bartime'][0]).strftime('%Y-%m-%d %H:%M:%S')
|
||
now=str(tickdata['datetime'][0])
|
||
if now>rdftm:
|
||
try:
|
||
oo=ofdatadict[symbol]
|
||
data_of(oo)
|
||
#print('oo',oo)
|
||
if symbol in quotedict.keys():
|
||
quotedict.pop(symbol)
|
||
if symbol in tickdatadict.keys():
|
||
tickdatadict.pop(symbol)
|
||
if symbol in ofdatadict.keys():
|
||
ofdatadict.pop(symbol)
|
||
except IOError as e:
|
||
print('rdftm捕获到异常',e)
|
||
tickdata['bartime'] = pd.to_datetime(tickdata['datetime'])
|
||
tickdata['open'] = tickdata['lastprice']
|
||
tickdata['high'] = tickdata['lastprice']
|
||
tickdata['low'] = tickdata['lastprice']
|
||
tickdata['close'] = tickdata['lastprice']
|
||
tickdata['starttime'] = tickdata['datetime']
|
||
else:
|
||
tickdata['bartime'] = rdf['bartime']
|
||
tickdata['open'] = rdf['open']
|
||
tickdata['high'] = max(tickdata['lastprice'].values,rdf['high'].values)
|
||
tickdata['low'] = min(tickdata['lastprice'].values,rdf['low'].values)
|
||
tickdata['close'] = tickdata['lastprice']
|
||
tickdata['volume']=df['vol']+rdf['volume'].values
|
||
tickdata['starttime'] = rdf['starttime']
|
||
else :
|
||
print('新bar的第一个tick进入')
|
||
tickdata['bartime'] = pd.to_datetime(tickdata['datetime'])
|
||
tickdata['open'] = tickdata['lastprice']
|
||
tickdata['high'] = tickdata['lastprice']
|
||
tickdata['low'] = tickdata['lastprice']
|
||
tickdata['close'] = tickdata['lastprice']
|
||
tickdata['starttime'] = tickdata['datetime']
|
||
except IOError as e:
|
||
print('捕获到异常',e)
|
||
|
||
|
||
tickdata['bartime'] = pd.to_datetime(tickdata['bartime'])
|
||
bardata = tickdata.resample(on = 'bartime', rule = BAR_RESAMPLE_RULE, label = 'right', closed = 'right').agg({'starttime':'first','symbol':'last','open':'first','high':'max','low':'min','close':'last','volume':'sum'}).reset_index(drop = False)
|
||
bardata =bardata.dropna().reset_index(drop = True)
|
||
bardata['bartime'] = pd.to_datetime(bardata['bartime'][0]).strftime('%Y-%m-%d %H:%M:%S')
|
||
tickdatadict[symbol]=bardata
|
||
tickdata['volume']=df['vol'].values
|
||
#print(bardata['symbol'].values,bardata['bartime'].values)
|
||
orderflow_df_new(tickdata,bardata,symbol)
|
||
# time.sleep(0.5)
|
||
|
||
#公众号:松鼠Quant
|
||
#主页:www.quant789.com
|
||
#本策略仅作学习交流使用,实盘交易盈亏投资者个人负责!!!
|
||
#版权归松鼠Quant所有,禁止转发、转卖源码违者必究。
|
||
|
||
def orderflow_df_new(df_tick,df_min,symbol):
|
||
startArray = pd.to_datetime(df_min['starttime']).values
|
||
voluememin= df_min['volume'].values
|
||
highs=df_min['high'].values
|
||
lows=df_min['low'].values
|
||
opens=df_min['open'].values
|
||
closes=df_min['close'].values
|
||
#endArray = pd.to_datetime(df_min['bartime']).values
|
||
endArray = df_min['bartime'].values
|
||
#print(endArray)
|
||
deltaArray = np.zeros((len(endArray),))
|
||
tTickArray = pd.to_datetime(df_tick['datetime']).values
|
||
bp1TickArray = df_tick['bid_p'].values
|
||
ap1TickArray = df_tick['ask_p'].values
|
||
lastTickArray = df_tick['lastprice'].values
|
||
volumeTickArray = df_tick['volume'].values
|
||
symbolarray = df_tick['symbol'].values
|
||
indexFinal = 0
|
||
for index,tEnd in enumerate(endArray):
|
||
dt=endArray[index]
|
||
start = startArray[index]
|
||
bidDict = {}
|
||
askDict = {}
|
||
bar_vol=voluememin[index]
|
||
bar_close=closes[index]
|
||
bar_open=opens[index]
|
||
bar_low=lows[index]
|
||
bar_high=highs[index]
|
||
bar_symbol=symbolarray[index]
|
||
# for indexTick in range(indexFinal,len(df_tick)):
|
||
# if tTickArray[indexTick] >= tEnd:
|
||
# break
|
||
# elif (tTickArray[indexTick] >= start) & (tTickArray[indexTick] < tEnd):
|
||
Bp = round(bp1TickArray[0],4)
|
||
Ap = round(ap1TickArray[0],4)
|
||
LastPrice = round(lastTickArray[0],4)
|
||
Volume = volumeTickArray[0]
|
||
if LastPrice >= Ap:
|
||
if str(LastPrice) in askDict.keys():
|
||
askDict[str(LastPrice)] += Volume
|
||
else:
|
||
askDict[str(LastPrice)] = Volume
|
||
if LastPrice <= Bp:
|
||
if str(LastPrice) in bidDict.keys():
|
||
bidDict[str(LastPrice)] += Volume
|
||
else:
|
||
bidDict[str(LastPrice)] = Volume
|
||
# indexFinal = indexTick
|
||
bidDictResult,askDictResult = process(bidDict,askDict,symbol)
|
||
bidDictResult=dict(sorted(bidDictResult.items(),key=operator.itemgetter(0)))
|
||
askDictResult=dict(sorted(askDictResult.items(),key=operator.itemgetter(0)))
|
||
prinslist=list(bidDictResult.keys())
|
||
asklist=list(askDictResult.values())
|
||
bidlist=list(bidDictResult.values())
|
||
delta=(sum(askDictResult.values()) - sum(bidDictResult.values()))
|
||
#print(prinslist,asklist,bidlist)
|
||
#print(len(prinslist),len(bidDictResult),len(askDictResult))
|
||
df=pd.DataFrame({'price':pd.Series([prinslist]),'Ask':pd.Series([asklist]),'Bid':pd.Series([bidlist])})
|
||
#df=pd.DataFrame({'price':pd.Series(bidDictResult.keys()),'Ask':pd.Series(askDictResult.values()),'Bid':pd.Series(bidDictResult.values())})
|
||
df['symbol']=bar_symbol
|
||
df['datetime']=dt
|
||
df['delta']=str(delta)
|
||
df['close']=bar_close
|
||
df['open']=bar_open
|
||
df['high']=bar_high
|
||
df['low']=bar_low
|
||
df['volume']=bar_vol
|
||
#df['ticktime']=tTickArray[0]
|
||
df['dj'] = GetOrderFlow_dj(df)
|
||
ofdatadict[symbol]=df
|
||
|
||
#公众号:松鼠Quant
|
||
#主页:www.quant789.com
|
||
#本策略仅作学习交流使用,实盘交易盈亏投资者个人负责!!!
|
||
#版权归松鼠Quant所有,禁止转发、转卖源码违者必究。
|
||
|
||
def GetOrderFlow_dj(kData):
|
||
Config = {
|
||
'Value1': 3,
|
||
'Value2': 3,
|
||
'Value3': 3,
|
||
'Value4': True,
|
||
}
|
||
aryData = kData
|
||
djcout = 0
|
||
|
||
# 遍历kData中的每一行,计算djcout指标
|
||
for index, row in aryData.iterrows():
|
||
kItem = aryData.iloc[index]
|
||
high = kItem['high']
|
||
low = kItem['low']
|
||
close = kItem['close']
|
||
open = kItem['open']
|
||
dtime = kItem['datetime']
|
||
price_s = kItem['price']
|
||
Ask_s = kItem['Ask']
|
||
Bid_s = kItem['Bid']
|
||
delta = kItem['delta']
|
||
|
||
price_s = price_s
|
||
Ask_s = Ask_s
|
||
Bid_s = Bid_s
|
||
|
||
gj = 0
|
||
xq = 0
|
||
gxx = 0
|
||
xxx = 0
|
||
|
||
# 遍历price_s中的每一个元素,计算相关指标
|
||
for i in np.arange(0, len(price_s), 1):
|
||
duiji = {
|
||
'price': 0,
|
||
'time': 0,
|
||
'longshort': 0,
|
||
}
|
||
|
||
if i == 0:
|
||
delta = delta
|
||
|
||
order= {
|
||
"Price":price_s[i],
|
||
"Bid":{ "Value":Bid_s[i]},
|
||
"Ask":{ "Value":Ask_s[i]}
|
||
}
|
||
#空头堆积
|
||
if i >= 0 and i < len(price_s) - 1:
|
||
if (order["Bid"]["Value"] > Ask_s[i + 1] * int(Config['Value1'])):
|
||
gxx += 1
|
||
gj += 1
|
||
if gj >= int(Config['Value2']) and Config['Value4'] == True:
|
||
duiji['price'] = price_s[i]
|
||
duiji['time'] = dtime
|
||
duiji['longshort'] = -1
|
||
if float(duiji['price']) > 0:
|
||
djcout += -1
|
||
else:
|
||
gj = 0
|
||
#多头堆积
|
||
if i >= 1 and i <= len(price_s) - 1:
|
||
if (order["Ask"]["Value"] > Bid_s[i - 1] * int(Config['Value1'])):
|
||
xq += 1
|
||
xxx += 1
|
||
if xq >= int(Config['Value2']) and Config['Value4'] == True:
|
||
duiji['price'] = price_s[i]
|
||
duiji['time'] = dtime
|
||
duiji['longshort'] = 1
|
||
if float(duiji['price']) > 0:
|
||
djcout += 1
|
||
else:
|
||
xq = 0
|
||
|
||
# 返回计算得到的djcout值
|
||
return djcout
|
||
|
||
#交易程序---------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
||
|
||
class MyTrader(TraderApiBase):
|
||
def __init__(self, broker_id, td_server, investor_id, password, app_id, auth_code, md_queue=None, page_dir='', private_resume_type=2, public_resume_type=2):
|
||
# Cython类不使用super().__init__()方式调用父类初始化方法
|
||
# TraderApiBase.__init__会由Cython自动处理
|
||
|
||
self.py=30 #设置委托价格的偏移,更加容易促成成交。仅螺纹,其他品种根据最小点波动,自己设置
|
||
self.cont_df=0
|
||
self.trailing_stop_percent = 0.005 #跟踪出场参数,从0.02减小到0.005
|
||
self.fixed_stop_loss_percent = 0.01 #固定出场参数
|
||
self.dj_X=1 #开仓的堆积参数
|
||
|
||
self.pos=0
|
||
self.Lots=1 #下单手数
|
||
self.short_trailing_stop_price = 0
|
||
self.long_trailing_stop_price = 0
|
||
self.sl_long_price=0
|
||
self.sl_shor_price=0
|
||
self.out_long=0
|
||
self.out_short=0
|
||
self.clearing_executed=False
|
||
self.day_closed = False # 添加日内平仓标志
|
||
self.kgdata = True #历史数据加载一次
|
||
self.holddata=True #持仓数据加载一次
|
||
self.use_ai_model = True # 是否使用AI模型来分析
|
||
|
||
# 新增止盈止损字典,按合约ID索引
|
||
self.stop_order_dict = {}
|
||
|
||
# 新增历史数据加载相关参数
|
||
self.load_history = False # 是否加载历史数据
|
||
self.history_rows = 30 # 默认加载30行历史数据
|
||
self.trader_rows = 10 # 当tader_df里的数据大于10行时开始计算指标及触发AI模型
|
||
|
||
def load_history_data(self, instrument_id):
|
||
"""
|
||
加载历史数据
|
||
|
||
Args:
|
||
instrument_id: 合约ID
|
||
"""
|
||
if not self.load_history:
|
||
return
|
||
|
||
try:
|
||
# 不再只提取英文字母,使用完整的合约代码
|
||
symbol = str(instrument_id)
|
||
json_file_path = f"traderdata/{symbol}_ofdata.json"
|
||
|
||
# 检查traderdata目录是否存在
|
||
if not os.path.exists("traderdata"):
|
||
print(f"traderdata目录不存在,创建目录")
|
||
os.makedirs("traderdata")
|
||
|
||
if os.path.exists(json_file_path):
|
||
try:
|
||
# 读取JSON文件,使用lines=True确保正确读取每行JSON
|
||
df = pd.read_json(json_file_path, lines=True)
|
||
|
||
if len(df) == 0:
|
||
print(f"历史数据文件为空: {json_file_path}")
|
||
print("将使用实时数据开始交易")
|
||
return False
|
||
|
||
# 如果数据行数超过设定的历史行数,只取最后N行
|
||
if len(df) > self.history_rows:
|
||
df = df.tail(self.history_rows)
|
||
print(f"历史数据超过设定行数,仅加载最后{self.history_rows}行")
|
||
|
||
# 更新全局trader_df
|
||
global trader_df
|
||
trader_df = df
|
||
|
||
print(f"\n===== 历史数据加载成功 =====")
|
||
print(f"合约: {instrument_id}")
|
||
print(f"数据行数: {len(df)}行")
|
||
print(f"数据时间范围: {df['datetime'].iloc[0]} 到 {df['datetime'].iloc[-1]}")
|
||
print(f"最新价格: {df['close'].iloc[-1]}")
|
||
print(f"最新成交量: {df['volume'].iloc[-1]}")
|
||
print("===========================\n")
|
||
|
||
# 更新cont_df
|
||
self.cont_df = len(df)
|
||
|
||
# 计算日均线
|
||
if len(df) > 0:
|
||
df['dayma'] = df['close'].mean()
|
||
|
||
# 计算累积的delta值
|
||
df['delta'] = df['delta'].astype(float)
|
||
df['delta累计'] = df['delta'].cumsum()
|
||
|
||
return True
|
||
except Exception as e:
|
||
print(f"\n===== 历史数据加载错误 =====")
|
||
print(f"合约: {instrument_id}")
|
||
print(f"读取JSON错误: {e}")
|
||
print("将使用实时数据开始交易")
|
||
print("===========================\n")
|
||
return False
|
||
else:
|
||
print(f"\n===== 历史数据加载提示 =====")
|
||
print(f"合约: {instrument_id}")
|
||
print(f"未找到历史数据文件: {json_file_path}")
|
||
print("将使用实时数据开始交易")
|
||
print("===========================\n")
|
||
return False
|
||
|
||
except Exception as e:
|
||
print(f"\n===== 历史数据加载错误 =====")
|
||
print(f"合约: {instrument_id}")
|
||
print(f"错误信息: {e}")
|
||
print("将使用实时数据开始交易")
|
||
print("===========================\n")
|
||
return False
|
||
|
||
#读取保存的数据
|
||
def read_to_csv(self,symbol):
|
||
# 文件夹路径和文件路径
|
||
# 使用完整的合约代码
|
||
symbol = str(symbol)
|
||
folder_path = "traderdata"
|
||
file_path = os.path.join(folder_path, f"{symbol}traderdata.csv")
|
||
# 如果文件夹不存在则创建
|
||
if not os.path.exists(folder_path):
|
||
os.makedirs(folder_path)
|
||
|
||
# 读取保留的模型数据CSV文件
|
||
if os.path.exists(file_path):
|
||
df = pd.read_csv(file_path)
|
||
if not df.empty and self.holddata==True:
|
||
# 选择最后一行数据
|
||
row = df.iloc[-1]
|
||
# 根据CSV文件的列名将数据赋值给相应的属性
|
||
self.pos = int(row['pos'])
|
||
self.short_trailing_stop_price = float(row['short_trailing_stop_price'])
|
||
self.long_trailing_stop_price = float(row['long_trailing_stop_price'])
|
||
self.sl_long_price = float(row['sl_long_price'])
|
||
self.sl_shor_price = float(row['sl_shor_price'])
|
||
# self.out_long = int(row['out_long'])
|
||
# self.out_short = int(row['out_short'])
|
||
print("找到历史交易数据文件,已经更新持仓,止损止盈数据", df.iloc[-1])
|
||
self.holddata=False
|
||
else:
|
||
pass
|
||
#print("没有找到历史交易数据文件", file_path)
|
||
#如果没有找到CSV,则初始化变量
|
||
|
||
pass
|
||
|
||
#保存数据
|
||
def save_to_csv(self,symbol):
|
||
# 使用完整的合约代码
|
||
symbol = str(symbol)
|
||
# 创建DataFrame
|
||
data = {
|
||
'datetime': [datetime.now().strftime('%Y-%m-%d %H:%M:%S')], # 使用当前时间
|
||
'pos': [self.pos],
|
||
'short_trailing_stop_price': [self.short_trailing_stop_price],
|
||
'long_trailing_stop_price': [self.long_trailing_stop_price],
|
||
'sl_long_price': [self.sl_long_price],
|
||
'sl_shor_price': [self.sl_shor_price],
|
||
}
|
||
|
||
df = pd.DataFrame(data)
|
||
|
||
# 将DataFrame保存到CSV文件
|
||
df.to_csv(f"traderdata/{symbol}traderdata.csv", index=False)
|
||
|
||
#每日收盘重置数据
|
||
def day_data_reset(self,data):
|
||
# 获取当前时间
|
||
current_time = datetime.now().time()
|
||
|
||
# 第一时间范围
|
||
clearing_time1_start = s_time(14,55)
|
||
clearing_time1_end = s_time(15,00)
|
||
|
||
# 第二时间范围
|
||
clearing_time2_start = s_time(2,25)
|
||
clearing_time2_end = s_time(2,30)
|
||
|
||
current_bid = float(data['BidPrice1']) # 当前买价
|
||
current_ask = float(data['AskPrice1']) # 当前卖价
|
||
|
||
# 创建一个标志变量,用于记录是否已经执行过
|
||
self.clearing_executed = False
|
||
# 检查当前时间第一个操作的时间范围内
|
||
if clearing_time1_start <= current_time <= clearing_time1_end and not self.clearing_executed :
|
||
self.clearing_executed = True # 设置标志变量为已执行
|
||
|
||
# 如果有持仓,强制平仓
|
||
if self.pos != 0:
|
||
print("交易日结束,开始强制平仓")
|
||
# 遍历所有合约发送平仓指令
|
||
for instrument_id in list(self.stop_order_dict.keys()):
|
||
stops = self.stop_order_dict[instrument_id]
|
||
|
||
# 平多仓
|
||
if stops['long']['position'] > 0:
|
||
print(f"发送平多仓指令: {instrument_id}, 手数: {stops['long']['position']}, 价格: {current_bid}")
|
||
self.insert_order(data['ExchangeID'], data['InstrumentID'],
|
||
current_bid - self.py,
|
||
stops['long']['position'],
|
||
b'1', b'3')
|
||
# 平空仓
|
||
if stops['short']['position'] > 0:
|
||
print(f"发送平空仓指令: {instrument_id}, 手数: {stops['short']['position']}, 价格: {current_ask}")
|
||
self.insert_order(data['ExchangeID'], data['InstrumentID'],
|
||
current_ask + self.py,
|
||
stops['short']['position'],
|
||
b'0', b'3')
|
||
|
||
# 清空所有合约的持仓信息
|
||
for instrument_id in list(self.stop_order_dict.keys()):
|
||
self.clear_position_info(instrument_id, 'all')
|
||
self.day_closed = True # 设置日内平仓标志
|
||
print("日内交易已结束,禁止开新仓")
|
||
|
||
# 检查当前时间是否在第二个操作的时间范围内
|
||
elif clearing_time2_start <= current_time <= clearing_time2_end and not self.clearing_executed :
|
||
self.clearing_executed = True # 设置标志变量为已执行
|
||
|
||
# 如果有持仓,强制平仓
|
||
if self.pos != 0:
|
||
print("交易日结束,开始强制平仓")
|
||
# 遍历所有合约发送平仓指令
|
||
for instrument_id in list(self.stop_order_dict.keys()):
|
||
stops = self.stop_order_dict[instrument_id]
|
||
|
||
# 平多仓
|
||
if stops['long']['position'] > 0:
|
||
print(f"发送平多仓指令: {instrument_id}, 手数: {stops['long']['position']}, 价格: {current_bid}")
|
||
self.insert_order(data['ExchangeID'], data['InstrumentID'],
|
||
current_bid - self.py,
|
||
stops['long']['position'],
|
||
b'1', b'3')
|
||
# 平空仓
|
||
if stops['short']['position'] > 0:
|
||
print(f"发送平空仓指令: {instrument_id}, 手数: {stops['short']['position']}, 价格: {current_ask}")
|
||
self.insert_order(data['ExchangeID'], data['InstrumentID'],
|
||
current_ask + self.py,
|
||
stops['short']['position'],
|
||
b'0', b'3')
|
||
|
||
# 清空所有合约的持仓信息
|
||
for instrument_id in list(self.stop_order_dict.keys()):
|
||
self.clear_position_info(instrument_id, 'all')
|
||
self.day_closed = True # 设置日内平仓标志
|
||
print("日内交易已结束,禁止开新仓")
|
||
else:
|
||
self.clearing_executed = False
|
||
# 在新交易日开始时重置日内平仓标志
|
||
if current_time < clearing_time1_start or (current_time > clearing_time1_end and current_time < clearing_time2_start):
|
||
self.day_closed = False
|
||
return self.clearing_executed
|
||
|
||
def OnRtnTrade(self, pTrade):
|
||
print("||成交回报||", pTrade)
|
||
|
||
# 获取成交信息
|
||
instrument_id = pTrade['InstrumentID'].decode()
|
||
direction = pTrade['Direction'].decode() # '0'为买入,'1'为卖出
|
||
offset_flag = pTrade['OffsetFlag'].decode() # '0'为开仓,'1'为平仓,'3'为平今
|
||
volume = pTrade['Volume']
|
||
price = pTrade['Price']
|
||
|
||
# 根据成交类型更新持仓信息
|
||
if offset_flag in ['1', '3']: # 平仓或平今
|
||
# 判断平的是多头还是空头
|
||
if direction == '1': # 卖出,平多头
|
||
print(f"平多头成交: {instrument_id}, 价格: {price}, 数量: {volume}")
|
||
# 清空多头持仓信息
|
||
self.clear_position_info(instrument_id, 'long')
|
||
else: # 买入,平空头
|
||
print(f"平空头成交: {instrument_id}, 价格: {price}, 数量: {volume}")
|
||
# 清空空头持仓信息
|
||
self.clear_position_info(instrument_id, 'short')
|
||
|
||
elif offset_flag == '0': # 开仓
|
||
if direction == '0': # 买入,开多头
|
||
print(f"开多头成交: {instrument_id}, 价格: {price}, 数量: {volume}")
|
||
# 如果有空头持仓,先清空
|
||
if instrument_id in self.stop_order_dict and self.stop_order_dict[instrument_id]['short']['position'] > 0:
|
||
self.clear_position_info(instrument_id, 'short')
|
||
|
||
# 设置多头持仓信息
|
||
sl_price = price * (1 - self.fixed_stop_loss_percent) # 默认止损价
|
||
tp_price = price * (1 + self.trailing_stop_percent) # 默认止盈价
|
||
|
||
# 设置初始跟踪止损价,与开仓价保持一定距离
|
||
initial_trailing_stop = price * (1 - self.trailing_stop_percent)
|
||
|
||
print(f"设置止损价: {sl_price}, 止盈价: {tp_price}, 跟踪止损价: {initial_trailing_stop}, 跟踪百分比: {self.trailing_stop_percent*100:.3f}%")
|
||
|
||
# 更新多头持仓信息,设置合理的跟踪止损初始值
|
||
self.update_stop_order_dict(instrument_id, 'long', volume, price, sl_price, tp_price, initial_trailing_stop)
|
||
self.pos = volume # 更新全局持仓状态
|
||
|
||
# 兼容旧代码
|
||
self.long_trailing_stop_price = initial_trailing_stop
|
||
self.sl_long_price = sl_price
|
||
self.save_to_csv(instrument_id)
|
||
|
||
else: # 卖出,开空头
|
||
print(f"开空头成交: {instrument_id}, 价格: {price}, 数量: {volume}")
|
||
# 如果有多头持仓,先清空
|
||
if instrument_id in self.stop_order_dict and self.stop_order_dict[instrument_id]['long']['position'] > 0:
|
||
self.clear_position_info(instrument_id, 'long')
|
||
|
||
# 设置空头持仓信息
|
||
sl_price = price * (1 + self.fixed_stop_loss_percent) # 默认止损价
|
||
tp_price = price * (1 - self.trailing_stop_percent) # 默认止盈价
|
||
|
||
# 设置初始跟踪止损价,与开仓价保持一定距离
|
||
initial_trailing_stop = price * (1 + self.trailing_stop_percent)
|
||
|
||
print(f"设置止损价: {sl_price}, 止盈价: {tp_price}, 跟踪止损价: {initial_trailing_stop}, 跟踪百分比: {self.trailing_stop_percent*100:.3f}%")
|
||
|
||
# 更新空头持仓信息,设置合理的跟踪止损初始值
|
||
self.update_stop_order_dict(instrument_id, 'short', self.Lots, price, sl_price, tp_price, initial_trailing_stop)
|
||
self.pos = -1 # 更新全局持仓状态
|
||
|
||
# 兼容旧代码
|
||
self.short_trailing_stop_price = initial_trailing_stop
|
||
self.sl_shor_price = sl_price
|
||
self.save_to_csv(instrument_id)
|
||
|
||
def OnRspOrderInsert(self, pInputOrder, pRspInfo, nRequestID, bIsLast):
|
||
print("||OnRspOrderInsert||", pInputOrder, pRspInfo, nRequestID, bIsLast)
|
||
|
||
#公众号:松鼠Quant
|
||
#主页:www.quant789.com
|
||
#本策略仅作学习交流使用,实盘交易盈亏投资者个人负责!!!
|
||
#版权归松鼠Quant所有,禁止转发、转卖源码违者必究。
|
||
|
||
#注意:运行前请先安装好algoplus,
|
||
# pip install AlgoPlus
|
||
#http://www.algo.plus/ctp/python/0103001.html
|
||
|
||
# 订单状态通知
|
||
def OnRtnOrder(self, pOrder):
|
||
print("||订单回报||", pOrder)
|
||
|
||
def Join(self):
|
||
data = None
|
||
# 记录上次加载止盈止损信息的时间和合约
|
||
last_load_time = {}
|
||
|
||
while True:
|
||
if self.status == 0:
|
||
|
||
while not self.md_queue.empty():
|
||
data = self.md_queue.get(block=False)
|
||
instrument_id = data['InstrumentID'].decode() # 品种代码
|
||
|
||
# 首次运行时加载历史数据
|
||
if self.kgdata:
|
||
self.load_history_data(instrument_id)
|
||
self.kgdata = False
|
||
|
||
# 加载该合约的止盈止损信息,避免频繁加载
|
||
current_time = time.time()
|
||
if instrument_id not in last_load_time or current_time - last_load_time.get(instrument_id, 0) > 60: # 每60秒才重新加载一次
|
||
self.load_stop_orders_from_file(instrument_id)
|
||
last_load_time[instrument_id] = current_time
|
||
|
||
# 检查止盈止损条件
|
||
self.check_stop_conditions(data)
|
||
|
||
# 在每个tick都检查和处理AI交易信号 - 移到这里以提高执行速度
|
||
if self.use_ai_model:
|
||
self.check_and_process_ai_signals(data)
|
||
|
||
# 原有代码...
|
||
self.read_to_csv(instrument_id)
|
||
self.day_data_reset(data)
|
||
tickcome(data)
|
||
#新K线开始,启动交易程序 and 保存行情数据
|
||
if len(trader_df)>self.cont_df and len(trader_df)>0:
|
||
# 计算日均线
|
||
trader_df['dayma'] = trader_df['close'].mean()
|
||
|
||
# 计算累积的delta值
|
||
trader_df['delta'] = trader_df['delta'].astype(float)
|
||
trader_df['delta累计'] = trader_df['delta'].cumsum()
|
||
|
||
# 检查文件是否存在
|
||
json_file_path = f"traderdata/{instrument_id}_ofdata.json"
|
||
if os.path.exists(json_file_path):
|
||
try:
|
||
# 读取现有数据
|
||
existing_df = pd.read_json(json_file_path, lines=True)
|
||
# 合并新数据
|
||
combined_df = pd.concat([existing_df, trader_df.tail(1)], ignore_index=True)
|
||
# 保存合并后的数据,使用lines=True确保每行是独立的JSON对象
|
||
combined_df.to_json(json_file_path, orient='records', force_ascii=False, lines=True)
|
||
except Exception as e:
|
||
print(f"读取或保存JSON文件时出错: {e}")
|
||
# 如果读取出错,直接保存当前数据
|
||
trader_df.to_json(json_file_path, orient='records', force_ascii=False, lines=True)
|
||
else:
|
||
# 创建新文件并保存整个DataFrame
|
||
trader_df.to_json(json_file_path, orient='records', force_ascii=False, lines=True)
|
||
|
||
# 更新跟踪止损价格 - 兼容旧版本代码
|
||
if self.long_trailing_stop_price >0 and self.pos>0:
|
||
self.long_trailing_stop_price = trader_df['low'].iloc[-1] if self.long_trailing_stop_price<trader_df['low'].iloc[-1] else self.long_trailing_stop_price
|
||
self.save_to_csv(instrument_id)
|
||
|
||
if self.short_trailing_stop_price >0 and self.pos<0:
|
||
self.short_trailing_stop_price = trader_df['high'].iloc[-1] if trader_df['high'].iloc[-1] <self.short_trailing_stop_price else self.short_trailing_stop_price
|
||
self.save_to_csv(instrument_id)
|
||
|
||
# 使用AI模型进行交易决策 - 只在bar完成时触发分析,不再立即执行信号
|
||
if self.use_ai_model:
|
||
# 检查是否在日内平仓后
|
||
if self.day_closed:
|
||
print("日内交易已结束,禁止开新仓")
|
||
return
|
||
# 仅在K线完成时启动AI分析线程,但不立即执行信号
|
||
global AI_THREAD_RUNNING
|
||
if not AI_THREAD_RUNNING and len(trader_df) > self.trader_rows:
|
||
AI_THREAD_RUNNING = True
|
||
ai_thread = threading.Thread(
|
||
target=self.background_model_call,
|
||
args=(trader_df, instrument_id)
|
||
)
|
||
ai_thread.daemon = True # 设置为守护线程,主程序退出时自动结束
|
||
ai_thread.start()
|
||
print("启动AI分析线程...")
|
||
|
||
print(trader_df)
|
||
self.cont_df=len(trader_df)
|
||
else:
|
||
time.sleep(1)
|
||
|
||
def background_model_call(self, data_df, instrument_id):
|
||
"""
|
||
在后台线程中调用大模型,并将交易信号放入队列
|
||
|
||
Args:
|
||
data_df: 包含订单流数据的DataFrame
|
||
instrument_id: 合约ID
|
||
"""
|
||
global AI_THREAD_RUNNING
|
||
|
||
start_time = datetime.now()
|
||
print(f"\n===== 开始AI分析 [{start_time.strftime('%H:%M:%S')}] =====")
|
||
print(f"正在分析合约: {instrument_id}")
|
||
|
||
try:
|
||
# 复制DataFrame以避免在不同线程间共享数据可能导致的问题
|
||
df_copy = data_df.copy()
|
||
|
||
# 输出分析的数据行数
|
||
print(f"分析数据行数: {len(df_copy)}")
|
||
print(f"最新价格: {df_copy['close'].iloc[-1] if len(df_copy) > 0 else 'N/A'}")
|
||
|
||
# 调用大模型获取交易信号
|
||
trading_signal = call_deepseek_model(df_copy, self) # 传递self参数
|
||
|
||
# 计算分析耗时
|
||
end_time = datetime.now()
|
||
elapsed = (end_time - start_time).total_seconds()
|
||
|
||
# 将交易信号和合约ID一起放入队列
|
||
signal_data = {
|
||
'signal': trading_signal,
|
||
'instrument_id': instrument_id,
|
||
'timestamp': end_time # 使用结束时间作为时间戳
|
||
}
|
||
|
||
AI_SIGNAL_QUEUE.put(signal_data)
|
||
print(f"AI模型分析完成,结果已放入队列,耗时: {elapsed:.2f}秒")
|
||
print(f"分析结果: {trading_signal}")
|
||
print("===== AI分析完成 =====\n")
|
||
except Exception as e:
|
||
end_time = datetime.now()
|
||
elapsed = (end_time - start_time).total_seconds()
|
||
print(f"AI模型分析线程出现异常: {e}")
|
||
print(f"异常详情:")
|
||
import traceback
|
||
traceback.print_exc()
|
||
print(f"分析失败,耗时: {elapsed:.2f}秒")
|
||
print("===== AI分析异常结束 =====\n")
|
||
finally:
|
||
# 标记线程已完成
|
||
AI_THREAD_RUNNING = False
|
||
|
||
|
||
#公众号:松鼠Quant
|
||
#主页:www.quant789.com
|
||
#本策略仅作学习交流使用,实盘交易盈亏投资者个人负责!!!
|
||
#版权归松鼠Quant所有,禁止转发、转卖源码违者必究。
|
||
|
||
#注意:运行前请先安装好algoplus,
|
||
# pip install AlgoPlus
|
||
#http://www.algo.plus/ctp/python/0103001.html
|
||
|
||
def check_and_process_ai_signals(self, data):
|
||
"""
|
||
检查并处理AI产生的交易信号
|
||
每个tick都会调用此函数,实现更快的交易信号响应
|
||
"""
|
||
if AI_SIGNAL_QUEUE.empty():
|
||
return
|
||
|
||
# 从队列中获取信号
|
||
signal_data = AI_SIGNAL_QUEUE.get()
|
||
trading_signal = signal_data['signal']
|
||
instrument_id = signal_data['instrument_id']
|
||
signal_time = signal_data['timestamp']
|
||
|
||
# 检查信号是否过期(超过15秒)
|
||
if (datetime.now() - signal_time).total_seconds() > 15:
|
||
print(f"AI信号已过期,忽略: {trading_signal}")
|
||
return
|
||
|
||
# 验证合约ID是否匹配
|
||
if instrument_id != data['InstrumentID'].decode():
|
||
# 如果合约不匹配,将信号放回队列以便后续处理
|
||
AI_SIGNAL_QUEUE.put(signal_data)
|
||
return
|
||
|
||
print(f"\n===== 执行AI模型交易信号 [{datetime.now().strftime('%H:%M:%S')}] =====")
|
||
print(f"信号生成时间: {signal_time.strftime('%H:%M:%S')}")
|
||
print(f"信号类型: {trading_signal.get('action', '不操作')}")
|
||
print(f"置信度: {trading_signal.get('confidence', 0)}")
|
||
print(f"理由: {trading_signal.get('reason', '')}")
|
||
|
||
# 根据AI模型返回的交易信号执行交易
|
||
action = trading_signal.get('action', '不操作')
|
||
confidence = trading_signal.get('confidence', 0)
|
||
reason = trading_signal.get('reason', '')
|
||
stop_loss = trading_signal.get('stop_loss', 0)
|
||
take_profit = trading_signal.get('take_profit', 0)
|
||
trailing_percent = trading_signal.get('trailing_percent', 0)
|
||
|
||
# 如果AI建议了有效的跟踪止损百分比,则更新参数
|
||
if 0.0001 <= trailing_percent <= 0.001:
|
||
old_percent = self.trailing_stop_percent
|
||
self.trailing_stop_percent = trailing_percent
|
||
print(f"更新跟踪止损百分比参数: {old_percent*100:.3f}% -> {trailing_percent*100:.3f}%")
|
||
|
||
# 获取现有持仓信息
|
||
if instrument_id not in self.stop_order_dict:
|
||
self.stop_order_dict[instrument_id] = {
|
||
'long': {'position': 0, 'entry_price': 0, 'stop_loss': 0, 'take_profit': 0, 'trailing_stop': 0},
|
||
'short': {'position': 0, 'entry_price': 0, 'stop_loss': 0, 'take_profit': 0, 'trailing_stop': 0}
|
||
}
|
||
|
||
current_stops = self.stop_order_dict[instrument_id]
|
||
|
||
# 如果持有多头或空头头寸,更新跟踪止损价
|
||
if current_stops['long']['position'] > 0:
|
||
entry_price = current_stops['long']['entry_price']
|
||
new_trailing_stop = float(data['BidPrice1']) * (1 - self.trailing_stop_percent)
|
||
old_trailing_stop = current_stops['long']['trailing_stop']
|
||
|
||
# 只有当新计算的跟踪止损价更高时才更新
|
||
if new_trailing_stop > old_trailing_stop:
|
||
self.update_stop_order_dict(instrument_id, 'long', None, None, None, None, new_trailing_stop)
|
||
print(f"已根据新参数更新多头跟踪止损价: {old_trailing_stop} -> {new_trailing_stop}")
|
||
|
||
# 兼容旧代码
|
||
self.long_trailing_stop_price = new_trailing_stop
|
||
self.save_to_csv(instrument_id)
|
||
|
||
elif current_stops['short']['position'] > 0:
|
||
entry_price = current_stops['short']['entry_price']
|
||
new_trailing_stop = float(data['AskPrice1']) * (1 + self.trailing_stop_percent)
|
||
old_trailing_stop = current_stops['short']['trailing_stop']
|
||
|
||
# 只有当新计算的跟踪止损价更低时才更新
|
||
if new_trailing_stop < old_trailing_stop or old_trailing_stop == 0:
|
||
self.update_stop_order_dict(instrument_id, 'short', None, None, None, None, new_trailing_stop)
|
||
print(f"已根据新参数更新空头跟踪止损价: {old_trailing_stop} -> {new_trailing_stop}")
|
||
|
||
# 兼容旧代码
|
||
self.short_trailing_stop_price = new_trailing_stop
|
||
self.save_to_csv(instrument_id)
|
||
|
||
# 只有当置信度大于等于5时才执行交易
|
||
if confidence >= 6:
|
||
print(f"开始执行交易,当前价格: 买一{data['BidPrice1']} / 卖一{data['AskPrice1']}")
|
||
|
||
# 获取现有持仓信息
|
||
if instrument_id not in self.stop_order_dict:
|
||
self.stop_order_dict[instrument_id] = {
|
||
'long': {'position': 0, 'entry_price': 0, 'stop_loss': 0, 'take_profit': 0, 'trailing_stop': 0},
|
||
'short': {'position': 0, 'entry_price': 0, 'stop_loss': 0, 'take_profit': 0, 'trailing_stop': 0}
|
||
}
|
||
|
||
current_stops = self.stop_order_dict[instrument_id]
|
||
|
||
if action == '开多' and current_stops['long']['position'] <= 0:
|
||
# 如果持有空头头寸,先平空
|
||
if current_stops['short']['position'] > 0:
|
||
print('执行平空操作')
|
||
self.insert_order(data['ExchangeID'], data['InstrumentID'],
|
||
data['AskPrice1']+self.py,
|
||
current_stops['short']['position'],
|
||
b'0', b'3')
|
||
# 清空空头持仓信息
|
||
self.clear_position_info(instrument_id, 'short')
|
||
|
||
# 开多
|
||
print('执行开多操作')
|
||
entry_price = float(data['AskPrice1'])
|
||
self.insert_order(data['ExchangeID'], data['InstrumentID'],
|
||
entry_price+self.py, self.Lots, b'0', b'0')
|
||
|
||
# 使用AI建议的止损止盈价格
|
||
sl_price = stop_loss if stop_loss > 0 else entry_price * (1 - self.fixed_stop_loss_percent)
|
||
tp_price = take_profit if take_profit > 0 else entry_price * (1 + self.trailing_stop_percent)
|
||
|
||
# 设置初始跟踪止损价,与开仓价保持一定距离
|
||
initial_trailing_stop = entry_price * (1 - self.trailing_stop_percent)
|
||
|
||
print(f"设置止损价: {sl_price}, 止盈价: {tp_price}, 跟踪止损价: {initial_trailing_stop}, 跟踪百分比: {self.trailing_stop_percent*100:.3f}%")
|
||
|
||
# 更新多头持仓信息,设置合理的跟踪止损初始值
|
||
self.update_stop_order_dict(instrument_id, 'long', self.Lots, entry_price, sl_price, tp_price, initial_trailing_stop)
|
||
self.pos = 1 # 更新全局持仓状态
|
||
|
||
# 兼容旧代码
|
||
self.long_trailing_stop_price = initial_trailing_stop
|
||
self.sl_long_price = sl_price
|
||
self.save_to_csv(instrument_id)
|
||
|
||
elif action == '开空' and current_stops['short']['position'] <= 0:
|
||
# 如果持有多头头寸,先平多
|
||
if current_stops['long']['position'] > 0:
|
||
print('执行平多操作')
|
||
self.insert_order(data['ExchangeID'], data['InstrumentID'],
|
||
data['BidPrice1']-self.py,
|
||
current_stops['long']['position'],
|
||
b'1', b'3')
|
||
# 清空多头持仓信息
|
||
self.clear_position_info(instrument_id, 'long')
|
||
|
||
# 开空
|
||
print('执行开空操作')
|
||
entry_price = float(data['BidPrice1'])
|
||
self.insert_order(data['ExchangeID'], data['InstrumentID'],
|
||
entry_price-self.py, self.Lots, b'1', b'0')
|
||
|
||
# 使用AI建议的止损止盈价格
|
||
sl_price = stop_loss if stop_loss > 0 else entry_price * (1 + self.fixed_stop_loss_percent)
|
||
tp_price = take_profit if take_profit > 0 else entry_price * (1 - self.trailing_stop_percent)
|
||
|
||
# 设置初始跟踪止损价,与开仓价保持一定距离
|
||
initial_trailing_stop = entry_price * (1 + self.trailing_stop_percent)
|
||
|
||
print(f"设置止损价: {sl_price}, 止盈价: {tp_price}, 跟踪止损价: {initial_trailing_stop}, 跟踪百分比: {self.trailing_stop_percent*100:.3f}%")
|
||
|
||
# 更新空头持仓信息,设置合理的跟踪止损初始值
|
||
self.update_stop_order_dict(instrument_id, 'short', self.Lots, entry_price, sl_price, tp_price, initial_trailing_stop)
|
||
self.pos = -1 # 更新全局持仓状态
|
||
|
||
# 兼容旧代码
|
||
self.short_trailing_stop_price = initial_trailing_stop
|
||
self.sl_shor_price = sl_price
|
||
self.save_to_csv(instrument_id)
|
||
|
||
elif action == '平多' and current_stops['long']['position'] > 0:
|
||
print('执行平多操作')
|
||
self.insert_order(data['ExchangeID'], data['InstrumentID'],
|
||
data['BidPrice1']-self.py,
|
||
current_stops['long']['position'],
|
||
b'1', b'3')
|
||
# 清空多头持仓信息
|
||
self.clear_position_info(instrument_id, 'long')
|
||
|
||
elif action == '平空' and current_stops['short']['position'] > 0:
|
||
print('执行平空操作')
|
||
self.insert_order(data['ExchangeID'], data['InstrumentID'],
|
||
data['AskPrice1']+self.py,
|
||
current_stops['short']['position'],
|
||
b'0', b'3')
|
||
# 清空空头持仓信息
|
||
self.clear_position_info(instrument_id, 'short')
|
||
|
||
# 如果AI建议调整止损止盈价格
|
||
elif action == '不操作':
|
||
if stop_loss > 0:
|
||
if current_stops['long']['position'] > 0: # 多头持仓
|
||
self.update_stop_order_dict(instrument_id, 'long', None, None, stop_loss, None, None)
|
||
print(f'已调整多头止损价: {stop_loss}')
|
||
|
||
# 兼容旧代码
|
||
self.sl_long_price = stop_loss
|
||
self.save_to_csv(instrument_id)
|
||
elif current_stops['short']['position'] > 0: # 空头持仓
|
||
self.update_stop_order_dict(instrument_id, 'short', None, None, stop_loss, None, None)
|
||
print(f'已调整空头止损价: {stop_loss}')
|
||
|
||
# 兼容旧代码
|
||
self.sl_shor_price = stop_loss
|
||
self.save_to_csv(instrument_id)
|
||
|
||
if take_profit > 0:
|
||
if current_stops['long']['position'] > 0: # 多头持仓
|
||
self.update_stop_order_dict(instrument_id, 'long', None, None, None, take_profit, None)
|
||
print(f'已调整多头止盈价: {take_profit}')
|
||
|
||
# 兼容旧代码
|
||
self.long_trailing_stop_price = take_profit
|
||
self.save_to_csv(instrument_id)
|
||
elif current_stops['short']['position'] > 0: # 空头持仓
|
||
self.update_stop_order_dict(instrument_id, 'short', None, None, None, take_profit, None)
|
||
print(f'已调整空头止盈价: {take_profit}')
|
||
|
||
# 兼容旧代码
|
||
self.short_trailing_stop_price = take_profit
|
||
self.save_to_csv(instrument_id)
|
||
|
||
print("===== 交易信号执行完成 =====\n")
|
||
else:
|
||
print(f"信号置信度{confidence}低于执行阈值(5),不执行交易")
|
||
print("===== 交易信号处理完成 =====\n")
|
||
|
||
#公众号:松鼠Quant
|
||
#主页:www.quant789.com
|
||
#本策略仅作学习交流使用,实盘交易盈亏投资者个人负责!!!
|
||
#版权归松鼠Quant所有,禁止转发、转卖源码违者必究。
|
||
|
||
#注意:运行前请先安装好algoplus,
|
||
# pip install AlgoPlus
|
||
#http://www.algo.plus/ctp/python/0103001.html
|
||
|
||
def format_data_for_llm(self, df):
|
||
"""
|
||
将DataFrame格式化为适合LLM分析的文本格式,包含历史交易信息
|
||
"""
|
||
# 提取最近几条记录
|
||
recent_data = df.tail(self.trader_rows)
|
||
|
||
# 构建基本信息
|
||
data_text = "订单流数据分析:\n\n"
|
||
|
||
# 提取最新行情数据
|
||
instrument_id = recent_data['symbol'].iloc[-1]
|
||
|
||
# 添加最新价格和交易量信息
|
||
data_text += f"当前时间: {recent_data['datetime'].iloc[-1]}\n"
|
||
data_text += f"当前价格: {recent_data['close'].iloc[-1]}\n"
|
||
data_text += f"开盘价: {recent_data['open'].iloc[-1]}\n"
|
||
data_text += f"最高价: {recent_data['high'].iloc[-1]}\n"
|
||
data_text += f"最低价: {recent_data['low'].iloc[-1]}\n"
|
||
data_text += f"成交量: {recent_data['volume'].iloc[-1]}\n"
|
||
|
||
# 计算价格趋势
|
||
if len(recent_data) >= 5:
|
||
price_trend = recent_data['close'].pct_change().tail(5).mean() * 100
|
||
data_text += f"价格短期趋势: {'上涨' if price_trend > 0 else '下跌'} ({price_trend:.2f}%)\n"
|
||
|
||
# 计算价格波动性
|
||
if len(recent_data) >= 5:
|
||
volatility = recent_data['close'].pct_change().tail(5).std() * 100
|
||
data_text += f"价格波动性: {volatility:.2f}%\n"
|
||
|
||
# 添加支撑和阻力位分析
|
||
recent_high = recent_data['high'].max()
|
||
recent_low = recent_data['low'].min()
|
||
data_text += f"近期阻力位: {recent_high}\n"
|
||
data_text += f"近期支撑位: {recent_low}\n"
|
||
|
||
# 添加均线分析 - 计算5、10、20均线
|
||
if len(df) >= 20:
|
||
# 计算均线
|
||
ma5 = df['close'].rolling(5).mean().iloc[-1]
|
||
ma10 = df['close'].rolling(10).mean().iloc[-1]
|
||
ma20 = df['close'].rolling(20).mean().iloc[-1]
|
||
|
||
data_text += f"MA5: {ma5:.2f}\n"
|
||
data_text += f"MA10: {ma10:.2f}\n"
|
||
data_text += f"MA20: {ma20:.2f}\n"
|
||
|
||
# 判断均线形态
|
||
if abs(ma5 - ma10) / ma10 < 0.001 and abs(ma10 - ma20) / ma20 < 0.001:
|
||
ma_pattern = "均线粘合"
|
||
elif ma5 > ma10 and ma10 > ma20:
|
||
ma_pattern = "多头排列"
|
||
elif ma5 < ma10 and ma10 < ma20:
|
||
ma_pattern = "空头排列"
|
||
elif ma5 > ma10 and ma10 < ma20:
|
||
ma_pattern = "金叉形态"
|
||
elif ma5 < ma10 and ma10 > ma20:
|
||
ma_pattern = "死叉形态"
|
||
else:
|
||
ma_pattern = "无明显形态"
|
||
|
||
data_text += f"均线形态: {ma_pattern}\n"
|
||
|
||
# 价格与均线关系
|
||
current_price = df['close'].iloc[-1]
|
||
data_text += f"价格相对MA5: {'上方' if current_price > ma5 else '下方'} ({(current_price/ma5-1)*100:.2f}%)\n"
|
||
data_text += f"价格相对MA10: {'上方' if current_price > ma10 else '下方'} ({(current_price/ma10-1)*100:.2f}%)\n"
|
||
data_text += f"价格相对MA20: {'上方' if current_price > ma20 else '下方'} ({(current_price/ma20-1)*100:.2f}%)\n"
|
||
|
||
# 日内超涨超跌分析
|
||
if len(df) >= 20:
|
||
# 计算日内振幅
|
||
daily_high = df['high'].iloc[-20:].max()
|
||
daily_low = df['low'].iloc[-20:].min()
|
||
daily_range = (daily_high - daily_low) / daily_low * 100
|
||
|
||
# 当前价格在日内范围的位置
|
||
current_in_range = (df['close'].iloc[-1] - daily_low) / (daily_high - daily_low) * 100 if (daily_high - daily_low) > 0 else 50
|
||
|
||
data_text += f"日内振幅: {daily_range:.2f}%\n"
|
||
data_text += f"价格在日内范围的位置: {current_in_range:.2f}% (0%为日低, 100%为日高)\n"
|
||
|
||
# 判断超涨超跌
|
||
if current_in_range > 85:
|
||
data_text += "日内状态: 可能超涨\n"
|
||
elif current_in_range < 15:
|
||
data_text += "日内状态: 可能超跌\n"
|
||
else:
|
||
data_text += "日内状态: 正常区间\n"
|
||
|
||
# 形态识别
|
||
if len(df) >= 20:
|
||
# 简单K线形态识别
|
||
recent_k = df.tail(5).copy()
|
||
|
||
# 计算实体和影线
|
||
recent_k['body'] = abs(recent_k['close'] - recent_k['open'])
|
||
recent_k['upper_shadow'] = recent_k.apply(lambda x: x['high'] - max(x['open'], x['close']), axis=1)
|
||
recent_k['lower_shadow'] = recent_k.apply(lambda x: min(x['open'], x['close']) - x['low'], axis=1)
|
||
|
||
# 最新K线特征
|
||
latest_k = recent_k.iloc[-1]
|
||
prev_k = recent_k.iloc[-2] if len(recent_k) > 1 else None
|
||
|
||
data_text += "\nK线形态分析:\n"
|
||
|
||
# 判断最新K线类型
|
||
if latest_k['body'] == 0:
|
||
k_type = "十字星"
|
||
elif latest_k['upper_shadow'] > 2 * latest_k['body'] and latest_k['lower_shadow'] < 0.5 * latest_k['body']:
|
||
k_type = "上影线长"
|
||
elif latest_k['lower_shadow'] > 2 * latest_k['body'] and latest_k['upper_shadow'] < 0.5 * latest_k['body']:
|
||
k_type = "下影线长"
|
||
elif latest_k['close'] > latest_k['open'] and latest_k['body'] > np.mean(recent_k['body']):
|
||
k_type = "大阳线"
|
||
elif latest_k['close'] < latest_k['open'] and latest_k['body'] > np.mean(recent_k['body']):
|
||
k_type = "大阴线"
|
||
elif latest_k['close'] > latest_k['open']:
|
||
k_type = "小阳线"
|
||
elif latest_k['close'] < latest_k['open']:
|
||
k_type = "小阴线"
|
||
else:
|
||
k_type = "普通K线"
|
||
|
||
data_text += f"最新K线类型: {k_type}\n"
|
||
|
||
# 判断简单组合形态
|
||
if prev_k is not None:
|
||
if prev_k['close'] < prev_k['open'] and latest_k['close'] > latest_k['open'] and latest_k['open'] <= prev_k['close'] and latest_k['close'] > prev_k['open']:
|
||
data_text += "组合形态: 可能构成看涨吞没形态\n"
|
||
elif prev_k['close'] > prev_k['open'] and latest_k['close'] < latest_k['open'] and latest_k['open'] >= prev_k['close'] and latest_k['close'] < prev_k['open']:
|
||
data_text += "组合形态: 可能构成看跌吞没形态\n"
|
||
elif prev_k['close'] < prev_k['open'] and latest_k['close'] > latest_k['open'] and latest_k['body'] > 1.5 * prev_k['body']:
|
||
data_text += "组合形态: 可能构成看涨势能增强\n"
|
||
elif prev_k['close'] > prev_k['open'] and latest_k['close'] < latest_k['open'] and latest_k['body'] > 1.5 * prev_k['body']:
|
||
data_text += "组合形态: 可能构成看跌势能增强\n"
|
||
|
||
# 添加订单流特定数据
|
||
data_text += f"\n订单流净值: {recent_data['delta'].iloc[-1]}\n"
|
||
data_text += f"订单流累计值: {recent_data['delta累计'].iloc[-1] if 'delta累计' in recent_data.columns else '无数据'}\n"
|
||
data_text += f"堆积指标: {recent_data['dj'].iloc[-1]}\n"
|
||
|
||
# 添加日均线数据
|
||
data_text += f"日均线: {recent_data['dayma'].iloc[-1] if 'dayma' in recent_data.columns else '无数据'}\n"
|
||
|
||
# 添加价格与日均线的关系
|
||
if 'dayma' in recent_data.columns:
|
||
price_above_ma = recent_data['close'].iloc[-1] > recent_data['dayma'].iloc[-1]
|
||
data_text += f"价格位于日均线: {'上方' if price_above_ma else '下方'}\n"
|
||
|
||
# 订单流全面分析
|
||
data_text += "\n订单流详细分析:\n"
|
||
|
||
# 计算订单流趋势
|
||
if len(recent_data) >= 5:
|
||
delta_values = recent_data['delta'].values
|
||
delta_trend = np.mean(np.diff(delta_values)) if len(delta_values) > 1 else 0
|
||
data_text += f"订单流趋势: {'增强中' if delta_trend > 0 else '减弱中'} (变化率: {delta_trend:.2f})\n"
|
||
|
||
# 计算订单流强度(使用绝对值的平均值)
|
||
delta_strength = np.mean(np.abs(recent_data['delta'].values))
|
||
data_text += f"订单流强度: {delta_strength:.2f}\n"
|
||
|
||
# 订单流指标超涨超跌分析
|
||
if len(df) >= 20:
|
||
delta_values = df['delta'].iloc[-20:].values
|
||
delta_mean = np.mean(delta_values)
|
||
delta_std = np.std(delta_values)
|
||
current_delta = df['delta'].iloc[-1]
|
||
|
||
# Z分数计算
|
||
if delta_std > 0:
|
||
delta_z = (current_delta - delta_mean) / delta_std
|
||
data_text += f"订单流偏离度(Z分数): {delta_z:.2f}\n"
|
||
|
||
if delta_z > 2:
|
||
data_text += "订单流状态: 可能超买\n"
|
||
elif delta_z < -2:
|
||
data_text += "订单流状态: 可能超卖\n"
|
||
else:
|
||
data_text += "订单流状态: 正常区间\n"
|
||
|
||
# 买卖力量对比
|
||
if 'bid_v' in recent_data.columns and 'ask_v' in recent_data.columns:
|
||
bid_power = recent_data['bid_v'].mean()
|
||
ask_power = recent_data['ask_v'].mean()
|
||
power_ratio = bid_power / ask_power if ask_power != 0 else float('inf')
|
||
data_text += f"买卖比例: {power_ratio:.2f} (>1买方强势,<1卖方强势)\n"
|
||
|
||
# 订单流累计趋势
|
||
if 'delta累计' in recent_data.columns and len(recent_data) >= 2:
|
||
cumulative_start = recent_data['delta累计'].iloc[0]
|
||
cumulative_end = recent_data['delta累计'].iloc[-1]
|
||
cumulative_change = cumulative_end - cumulative_start
|
||
data_text += f"累计订单流变化: {cumulative_change:.2f} (从 {cumulative_start:.2f} 到 {cumulative_end:.2f})\n"
|
||
|
||
# 订单流波动性
|
||
delta_volatility = np.std(recent_data['delta'].values)
|
||
data_text += f"订单流波动性: {delta_volatility:.2f}\n"
|
||
|
||
# 订单流与价格相关性
|
||
if len(recent_data) >= 10: # 确保有足够数据计算相关性
|
||
price_changes = recent_data['close'].pct_change().dropna()
|
||
delta_changes = recent_data['delta'].iloc[1:].reset_index(drop=True) # 对齐索引
|
||
if len(price_changes) > 0 and len(delta_changes) == len(price_changes):
|
||
try:
|
||
correlation = np.corrcoef(price_changes, delta_changes)[0, 1] if len(price_changes) > 1 else 0
|
||
data_text += f"订单流与价格相关性: {correlation:.2f}\n"
|
||
data_text += f"相关性解读: {'强正相关' if correlation > 0.7 else '正相关' if correlation > 0.3 else '弱相关' if correlation > -0.3 else '负相关' if correlation > -0.7 else '强负相关'}\n"
|
||
except:
|
||
data_text += "订单流与价格相关性: 计算错误\n"
|
||
|
||
# 订单流势能分析(最近几分钟的方向)
|
||
recent_deltas = recent_data['delta'].tail(5).values
|
||
positive_count = sum(1 for x in recent_deltas if x > 0)
|
||
negative_count = sum(1 for x in recent_deltas if x < 0)
|
||
data_text += f"最近订单流方向: {'多头主导' if positive_count > negative_count else '空头主导' if positive_count < negative_count else '方向不明'} ({positive_count}正/{negative_count}负)\n"
|
||
|
||
# 订单流强弱可视化
|
||
delta_last_5 = recent_data['delta'].tail(5).values
|
||
strength_visual = ""
|
||
for val in delta_last_5:
|
||
if val > 3:
|
||
strength_visual += "↑↑ "
|
||
elif val > 0:
|
||
strength_visual += "↑ "
|
||
elif val < -3:
|
||
strength_visual += "↓↓ "
|
||
elif val < 0:
|
||
strength_visual += "↓ "
|
||
else:
|
||
strength_visual += "→ "
|
||
data_text += f"订单流强弱可视化 (最近5分钟): {strength_visual}\n"
|
||
|
||
# 添加最近几条记录的趋势
|
||
data_text += "\n最近几条记录的趋势:\n"
|
||
|
||
# 添加最近5条记录的关键指标变化
|
||
for i in range(min(5, len(recent_data))):
|
||
idx = -(i+1)
|
||
data_text += f"记录 {i+1}: 时间={recent_data['datetime'].iloc[idx]}, 价格={recent_data['close'].iloc[idx]}, "
|
||
data_text += f"订单流净值={recent_data['delta'].iloc[idx]}, 堆积={recent_data['dj'].iloc[idx]}\n"
|
||
|
||
# 添加当前持仓信息,使用新的止盈止损字典
|
||
data_text += "\n当前持仓状态:\n"
|
||
|
||
# 获取该合约的止盈止损信息
|
||
stops = self.stop_order_dict.get(instrument_id, {
|
||
'long': {'position': 0, 'entry_price': 0, 'stop_loss': 0, 'take_profit': 0, 'trailing_stop': 0},
|
||
'short': {'position': 0, 'entry_price': 0, 'stop_loss': 0, 'take_profit': 0, 'trailing_stop': 0}
|
||
})
|
||
|
||
# 获取多头和空头持仓信息
|
||
long_pos = stops.get('long', {}).get('position', 0)
|
||
short_pos = stops.get('short', {}).get('position', 0)
|
||
|
||
# 判断当前持仓方向
|
||
if long_pos > 0:
|
||
data_text += f"持仓方向: 多头\n"
|
||
data_text += f"持仓数量: {long_pos}\n"
|
||
data_text += f"开仓价格: {stops['long']['entry_price']}\n"
|
||
data_text += f"止损价格: {stops['long']['stop_loss']}\n"
|
||
data_text += f"止盈价格: {stops['long']['take_profit']}\n"
|
||
data_text += f"跟踪止损价: {stops['long']['trailing_stop']}\n"
|
||
|
||
# 计算当前盈亏
|
||
current_price = recent_data['close'].iloc[-1]
|
||
profit_percent = (current_price - stops['long']['entry_price']) / stops['long']['entry_price'] * 100
|
||
data_text += f"当前盈亏: {profit_percent:.2f}%\n"
|
||
elif short_pos > 0:
|
||
data_text += f"持仓方向: 空头\n"
|
||
data_text += f"持仓数量: {short_pos}\n"
|
||
data_text += f"开仓价格: {stops['short']['entry_price']}\n"
|
||
data_text += f"止损价格: {stops['short']['stop_loss']}\n"
|
||
data_text += f"止盈价格: {stops['short']['take_profit']}\n"
|
||
data_text += f"跟踪止损价: {stops['short']['trailing_stop']}\n"
|
||
|
||
# 计算当前盈亏
|
||
current_price = recent_data['close'].iloc[-1]
|
||
profit_percent = (stops['short']['entry_price'] - current_price) / stops['short']['entry_price'] * 100
|
||
data_text += f"当前盈亏: {profit_percent:.2f}%\n"
|
||
else:
|
||
data_text += "持仓方向: 空仓\n"
|
||
data_text += "持仓数量: 0\n"
|
||
|
||
# 添加风险管理参数信息
|
||
data_text += "\n风险管理参数设置:\n"
|
||
data_text += f"固定止损百分比: {self.fixed_stop_loss_percent * 100:.2f}%\n"
|
||
data_text += f"跟踪止损百分比: {self.trailing_stop_percent * 100:.2f}%\n"
|
||
|
||
# 添加交易建议提示
|
||
data_text += "\n请根据以上信息,分析当前市场状态并给出交易建议。需要考虑:\n"
|
||
data_text += "1. 当前持仓状态是否合理\n"
|
||
data_text += "2. 是否需要调整止损止盈位置\n"
|
||
data_text += "3. 是否需要平仓或反手\n"
|
||
data_text += "4. 是否适合开新仓\n"
|
||
data_text += "5. 是否需要调整跟踪止损百分比参数(范围建议:0.0001-0.001)\n"
|
||
data_text += "6. 是否出现抄底摸顶机会\n"
|
||
data_text += "7. 是否存在日内波段交易机会\n"
|
||
|
||
return data_text
|
||
|
||
def update_stop_order_dict(self, instrument_id, direction, position, entry_price=None, stop_loss=None, take_profit=None, trailing_stop=None):
|
||
"""
|
||
更新止盈止损字典
|
||
|
||
Args:
|
||
instrument_id: 合约ID
|
||
direction: 方向 'long' 或 'short'
|
||
position: 持仓手数
|
||
entry_price: 开仓价格
|
||
stop_loss: 止损价格
|
||
take_profit: 止盈价格
|
||
trailing_stop: 跟踪止损价格
|
||
"""
|
||
# 确保合约ID在字典中存在
|
||
if instrument_id not in self.stop_order_dict:
|
||
self.stop_order_dict[instrument_id] = {
|
||
'long': {'position': 0, 'entry_price': 0, 'stop_loss': 0, 'take_profit': 0, 'trailing_stop': 0},
|
||
'short': {'position': 0, 'entry_price': 0, 'stop_loss': 0, 'take_profit': 0, 'trailing_stop': 0}
|
||
}
|
||
|
||
# 更新指定方向的数据,仅更新提供的参数
|
||
if position is not None:
|
||
self.stop_order_dict[instrument_id][direction]['position'] = position
|
||
|
||
if entry_price is not None:
|
||
self.stop_order_dict[instrument_id][direction]['entry_price'] = entry_price
|
||
|
||
if stop_loss is not None:
|
||
self.stop_order_dict[instrument_id][direction]['stop_loss'] = stop_loss
|
||
|
||
if take_profit is not None:
|
||
self.stop_order_dict[instrument_id][direction]['take_profit'] = take_profit
|
||
|
||
if trailing_stop is not None:
|
||
self.stop_order_dict[instrument_id][direction]['trailing_stop'] = trailing_stop
|
||
|
||
# 保存到文件
|
||
self.save_stop_orders_to_file(instrument_id)
|
||
|
||
def save_stop_orders_to_file(self, instrument_id):
|
||
"""将止盈止损信息保存到文件"""
|
||
# 使用完整的合约代码
|
||
symbol = str(instrument_id)
|
||
folder_path = "traderdata"
|
||
file_path = os.path.join(folder_path, f"{symbol}_stops.json")
|
||
|
||
# 如果文件夹不存在则创建
|
||
if not os.path.exists(folder_path):
|
||
os.makedirs(folder_path)
|
||
|
||
# 保存字典到JSON文件
|
||
with open(file_path, 'w') as f:
|
||
json.dump(self.stop_order_dict.get(instrument_id, {}), f, indent=4)
|
||
|
||
# 获取该合约的止盈止损信息
|
||
stops = self.stop_order_dict.get(instrument_id, {
|
||
'long': {'position': 0, 'entry_price': 0, 'stop_loss': 0, 'take_profit': 0, 'trailing_stop': 0},
|
||
'short': {'position': 0, 'entry_price': 0, 'stop_loss': 0, 'take_profit': 0, 'trailing_stop': 0}
|
||
})
|
||
|
||
# 打印详细信息
|
||
print(f"\n===== 已更新止盈止损信息: {instrument_id} =====")
|
||
print(f"多头持仓: {stops['long']['position']} 手")
|
||
print(f"多头入场价: {stops['long']['entry_price']}")
|
||
print(f"多头止损价: {stops['long']['stop_loss']}")
|
||
print(f"多头止盈价: {stops['long']['take_profit']}")
|
||
print(f"多头跟踪止损: {stops['long']['trailing_stop']}")
|
||
print(f"空头持仓: {stops['short']['position']} 手")
|
||
print(f"空头入场价: {stops['short']['entry_price']}")
|
||
print(f"空头止损价: {stops['short']['stop_loss']}")
|
||
print(f"空头止盈价: {stops['short']['take_profit']}")
|
||
print(f"空头跟踪止损: {stops['short']['trailing_stop']}")
|
||
print("======================================\n")
|
||
|
||
def load_stop_orders_from_file(self, instrument_id):
|
||
"""从文件加载止盈止损信息"""
|
||
# 如果合约ID已经在字典中,直接返回,避免重复加载
|
||
if instrument_id in self.stop_order_dict:
|
||
return True
|
||
|
||
# 使用完整的合约代码
|
||
symbol = str(instrument_id)
|
||
folder_path = "traderdata"
|
||
file_path = os.path.join(folder_path, f"{symbol}_stops.json")
|
||
|
||
if os.path.exists(file_path):
|
||
try:
|
||
with open(file_path, 'r') as f:
|
||
stops_data = json.load(f)
|
||
|
||
# 更新字典
|
||
self.stop_order_dict[instrument_id] = stops_data
|
||
print(f"首次加载止盈止损信息: {instrument_id}")
|
||
return True
|
||
except Exception as e:
|
||
print(f"加载止盈止损信息失败: {e}")
|
||
|
||
# 如果文件不存在,初始化空字典结构
|
||
if instrument_id not in self.stop_order_dict:
|
||
self.stop_order_dict[instrument_id] = {
|
||
'long': {'position': 0, 'entry_price': 0, 'stop_loss': 0, 'take_profit': 0, 'trailing_stop': 0},
|
||
'short': {'position': 0, 'entry_price': 0, 'stop_loss': 0, 'take_profit': 0, 'trailing_stop': 0}
|
||
}
|
||
print(f"初始化止盈止损结构: {instrument_id}")
|
||
|
||
return False
|
||
|
||
def check_stop_conditions(self, data):
|
||
"""检查是否满足止盈止损条件"""
|
||
instrument_id = data['InstrumentID'].decode()
|
||
|
||
# 如果该合约不在止盈止损字典中,直接返回
|
||
if instrument_id not in self.stop_order_dict:
|
||
return
|
||
|
||
current_bid = float(data['BidPrice1']) # 当前买价
|
||
current_ask = float(data['AskPrice1']) # 当前卖价
|
||
|
||
stops = self.stop_order_dict[instrument_id]
|
||
|
||
# 检查多头止盈止损
|
||
if stops['long']['position'] > 0:
|
||
# 检查止损
|
||
if stops['long']['stop_loss'] > 0 and current_bid <= stops['long']['stop_loss']:
|
||
print(f"触发多头止损: {instrument_id}, 价格: {current_bid}, 止损价: {stops['long']['stop_loss']}")
|
||
self.insert_order(data['ExchangeID'], data['InstrumentID'], current_bid-self.py,
|
||
stops['long']['position'], b'1', b'3')
|
||
# 清空多头持仓信息
|
||
self.clear_position_info(instrument_id, 'long')
|
||
self.pos = 0 # 更新全局持仓状态
|
||
|
||
# 检查跟踪止损 - 新增的逻辑
|
||
elif stops['long']['trailing_stop'] > 0 and current_bid < stops['long']['trailing_stop']:
|
||
print(f"触发多头跟踪止损: {instrument_id}, 价格: {current_bid}, 跟踪止损价: {stops['long']['trailing_stop']}")
|
||
self.insert_order(data['ExchangeID'], data['InstrumentID'], current_bid-self.py,
|
||
stops['long']['position'], b'1', b'3')
|
||
# 清空多头持仓信息
|
||
self.clear_position_info(instrument_id, 'long')
|
||
self.pos = 0 # 更新全局持仓状态
|
||
|
||
# 检查止盈
|
||
elif stops['long']['take_profit'] > 0 and current_bid >= stops['long']['take_profit']:
|
||
print(f"触发多头止盈: {instrument_id}, 价格: {current_bid}, 止盈价: {stops['long']['take_profit']}")
|
||
self.insert_order(data['ExchangeID'], data['InstrumentID'], current_bid-self.py,
|
||
stops['long']['position'], b'1', b'3')
|
||
# 清空多头持仓信息
|
||
self.update_stop_order_dict(instrument_id, 'long', 0, 0, 0, 0)
|
||
self.pos = 0 # 更新全局持仓状态
|
||
|
||
# 更新跟踪止损价 - 只在价格上涨时更新
|
||
elif stops['long']['trailing_stop'] > 0:
|
||
# 只有当前价格比之前设置的跟踪止损价高一定幅度时才更新
|
||
new_trailing_stop = current_bid * (1 - self.trailing_stop_percent)
|
||
if new_trailing_stop > stops['long']['trailing_stop']:
|
||
self.update_stop_order_dict(instrument_id, 'long', None, None, None, None, new_trailing_stop)
|
||
print(f"更新多头跟踪止损: {instrument_id}, 新止损价: {new_trailing_stop}")
|
||
|
||
# 检查空头止盈止损
|
||
if stops['short']['position'] > 0:
|
||
# 检查止损
|
||
if stops['short']['stop_loss'] > 0 and current_ask >= stops['short']['stop_loss']:
|
||
print(f"触发空头止损: {instrument_id}, 价格: {current_ask}, 止损价: {stops['short']['stop_loss']}")
|
||
self.insert_order(data['ExchangeID'], data['InstrumentID'], current_ask+self.py,
|
||
stops['short']['position'], b'0', b'3')
|
||
# 清空空头持仓信息
|
||
self.update_stop_order_dict(instrument_id, 'short', 0, 0, 0, 0, 0)
|
||
self.pos = 0 # 更新全局持仓状态
|
||
|
||
# 检查跟踪止损 - 新增的逻辑
|
||
elif stops['short']['trailing_stop'] > 0 and current_ask > stops['short']['trailing_stop']:
|
||
print(f"触发空头跟踪止损: {instrument_id}, 价格: {current_ask}, 跟踪止损价: {stops['short']['trailing_stop']}")
|
||
self.insert_order(data['ExchangeID'], data['InstrumentID'], current_ask+self.py,
|
||
stops['short']['position'], b'0', b'3')
|
||
# 清空空头持仓信息
|
||
self.clear_position_info(instrument_id, 'short')
|
||
self.pos = 0 # 更新全局持仓状态
|
||
|
||
# 检查止盈
|
||
elif stops['short']['take_profit'] > 0 and current_ask <= stops['short']['take_profit']:
|
||
print(f"触发空头止盈: {instrument_id}, 价格: {current_ask}, 止盈价: {stops['short']['take_profit']}")
|
||
self.insert_order(data['ExchangeID'], data['InstrumentID'], current_ask+self.py,
|
||
stops['short']['position'], b'0', b'3')
|
||
# 清空空头持仓信息
|
||
self.update_stop_order_dict(instrument_id, 'short', 0, 0, 0, 0, 0)
|
||
self.pos = 0 # 更新全局持仓状态
|
||
|
||
# 更新跟踪止损价 - 只在价格下跌时更新
|
||
elif stops['short']['trailing_stop'] > 0:
|
||
# 只有当前价格比之前设置的跟踪止损价低一定幅度时才更新
|
||
new_trailing_stop = current_ask * (1 + self.trailing_stop_percent)
|
||
if new_trailing_stop < stops['short']['trailing_stop'] or stops['short']['trailing_stop'] == 0:
|
||
self.update_stop_order_dict(instrument_id, 'short', None, None, None, None, new_trailing_stop)
|
||
print(f"更新空头跟踪止损: {instrument_id}, 新止损价: {new_trailing_stop}")
|
||
|
||
#保存数据
|
||
|
||
def clear_position_info(self, instrument_id, direction):
|
||
"""
|
||
清空指定合约和方向的持仓信息
|
||
|
||
Args:
|
||
instrument_id: 合约ID
|
||
direction: 方向 'long' 或 'short' 或 'all'
|
||
"""
|
||
# 确保合约ID在字典中存在
|
||
if instrument_id not in self.stop_order_dict:
|
||
return
|
||
|
||
if direction == 'long' or direction == 'all':
|
||
# 清空多头持仓信息
|
||
self.stop_order_dict[instrument_id]['long'] = {
|
||
'position': 0,
|
||
'entry_price': 0,
|
||
'stop_loss': 0,
|
||
'take_profit': 0,
|
||
'trailing_stop': 0
|
||
}
|
||
# 兼容旧代码
|
||
if direction == 'long':
|
||
self.long_trailing_stop_price = 0
|
||
self.sl_long_price = 0
|
||
|
||
if direction == 'short' or direction == 'all':
|
||
# 清空空头持仓信息
|
||
self.stop_order_dict[instrument_id]['short'] = {
|
||
'position': 0,
|
||
'entry_price': 0,
|
||
'stop_loss': 0,
|
||
'take_profit': 0,
|
||
'trailing_stop': 0
|
||
}
|
||
# 兼容旧代码
|
||
if direction == 'short':
|
||
self.short_trailing_stop_price = 0
|
||
self.sl_shor_price = 0
|
||
|
||
# 同步全局持仓状态
|
||
if direction == 'all':
|
||
self.pos = 0
|
||
self.long_trailing_stop_price = 0
|
||
self.short_trailing_stop_price = 0
|
||
self.sl_long_price = 0
|
||
self.sl_shor_price = 0
|
||
|
||
# 保存到文件
|
||
self.save_stop_orders_to_file(instrument_id)
|
||
self.save_to_csv(instrument_id)
|
||
|
||
print(f"已清空{instrument_id}的{direction}持仓信息")
|
||
|
||
# 修改call_deepseek_model函数,移除JSON格式输出的要求,改为从普通文本响应中提取交易信号。
|
||
def call_deepseek_model(data_df, trader_instance, max_retries=2):
|
||
"""
|
||
调用deepseek-r1大模型分析订单流数据
|
||
|
||
Args:
|
||
data_df: 包含订单流数据的DataFrame
|
||
trader_instance: MyTrader实例,用于访问持仓信息
|
||
max_retries: 最大重试次数
|
||
|
||
Returns:
|
||
dict: 包含交易信号的字典
|
||
"""
|
||
# 直接从环境变量获取API密钥
|
||
api_key = os.environ.get("OPENAI_API_KEY") or GLOBAL_LLM_CONFIG.get('api_key')
|
||
base_url = GLOBAL_LLM_CONFIG.get('base_url', "https://api.deepseek.com")
|
||
model_name = GLOBAL_LLM_CONFIG.get('model_name', "deepseek-reasoner")
|
||
|
||
# 检查API密钥是否为空
|
||
if not api_key:
|
||
print("错误: API密钥未设置,请在main函数中配置GLOBAL_LLM_CONFIG['api_key']")
|
||
return {"action": "不操作", "reason": "API密钥未设置", "confidence": 0, "stop_loss": 0, "take_profit": 0}
|
||
|
||
# 将DataFrame转换为可读性好的文本,传递trader_instance
|
||
data_text = trader_instance.format_data_for_llm(data_df)
|
||
|
||
# 构建提示词
|
||
prompt = f"""
|
||
作为期货交易员,基于以下市场数据做出交易决策:
|
||
|
||
{data_text}
|
||
|
||
请重点考虑以下关键因素:
|
||
1. 均线形态(5、10、20均线)的排列状态及价格位置
|
||
2. 价格趋势、波动性与关键支撑阻力位
|
||
3. 日内超涨超跌状态,寻找高胜率反转交易机会
|
||
4. K线组合形态,尤其关注反转信号和动量变化
|
||
5. 订单流趋势变化与力度
|
||
6. 买卖力量对比和资金流向
|
||
7. 当前持仓状态与盈亏情况
|
||
8. 风险回报比与止损止盈设置合理性
|
||
9. 适合的交易时机与市场环境判断
|
||
|
||
【风险控制原则】:
|
||
- 严格控制每笔交易亏损不超过本金的1%
|
||
- 止损位设置应紧凑,一般不超过开仓价的0.3%-0.8%
|
||
- 风险回报比至少为1:1.5,建议争取1:2或更高
|
||
- 在不明确的市场环境下,以保护资金为第一要务
|
||
|
||
请根据市场状态和持仓情况,给出明确的交易指令:
|
||
1. 交易方向: 开多/开空/平多/平空/不操作
|
||
2. 执行理由: 详细说明交易逻辑,结合技术形态、价格位置、订单流特征和市场状态,包括对当前持仓的处理方案
|
||
3. 置信度: 1-10的数字,必须是整数
|
||
4. 止损价: 明确的止损价格(必须是数字),确保亏损控制在合理范围内,不能过大
|
||
5. 止盈价: 明确的止盈价格(必须是数字),应至少是止损距离的1.5倍
|
||
6. 跟踪止损百分比: 0.0001-0.001之间的数字,根据市场波动性调整
|
||
|
||
请按以下格式返回交易决策,格式必须严格匹配:
|
||
action: 开多/开空/平多/平空/不操作
|
||
reason: 交易理由
|
||
confidence: 置信度(1-10)
|
||
stop_loss: 止损价
|
||
take_profit: 止盈价
|
||
trailing_percent: 跟踪止损百分比(0.0001-0.001)
|
||
"""
|
||
|
||
system_prompt = {"role": "system",
|
||
"content": "你是一位经验丰富的期货交易员,拥有多年实盘交易经验,精通订单流分析和技术形态识别。你的交易风格注重实战执行,善于制定明确的入场出场策略和风险控制方案。特别擅长日内波段交易和抄底摸顶策略,能准确捕捉超涨超跌反转机会和均线形态变化带来的交易信号,并能迅速做出果断决策。你综合考虑市场情绪、技术指标与价格行为,在保护资金安全的前提下寻求最佳交易机会。根据当前市场状态和持仓情况,你能给出明确的交易执行方案、止损止盈设置以及仓位管理建议。请确保以指定的格式返回交易决策。"}
|
||
|
||
# 添加重试机制
|
||
retries = 0
|
||
while retries <= max_retries:
|
||
try:
|
||
# 如果不是第一次尝试,输出重试信息
|
||
if retries > 0:
|
||
print(f"正在进行第 {retries} 次重试...")
|
||
|
||
# 调试信息
|
||
print(f"使用API参数: base_url={base_url}, model={model_name}")
|
||
|
||
# 添加明显的提示信息,表示正在调用大模型API
|
||
print("\n============================================")
|
||
print("【正在调用大模型API进行交易分析,请稍候...】")
|
||
print("============================================\n")
|
||
|
||
# 记录开始时间
|
||
api_start_time = time.time()
|
||
|
||
# 使用OpenAI客户端格式调用API
|
||
client = OpenAI(api_key=api_key, base_url=base_url)
|
||
|
||
response = client.chat.completions.create(
|
||
model=model_name,
|
||
messages=[
|
||
system_prompt,
|
||
{"role": "user", "content": prompt}
|
||
],
|
||
temperature=0.1,
|
||
# 移除JSON格式输出的要求
|
||
max_tokens=8192,
|
||
timeout=60 # 将超时时间从30秒增加到60秒
|
||
)
|
||
|
||
# 计算API调用耗时
|
||
api_elapsed = time.time() - api_start_time
|
||
|
||
# 提取模型输出的内容
|
||
model_response = response.choices[0].message.content
|
||
print(f"模型响应耗时: {api_elapsed:.2f}秒")
|
||
print("模型响应前100字符: " + model_response[:100] + "...")
|
||
|
||
# 添加明显的提示信息,表示API调用已完成
|
||
print("\n============================================")
|
||
print("【大模型API调用完成】")
|
||
print("============================================\n")
|
||
|
||
# 从文本中解析出交易信号
|
||
try:
|
||
trading_signal = parse_trading_signal(model_response)
|
||
return trading_signal
|
||
except Exception as parse_err:
|
||
print(f"解析模型响应出错: {parse_err}")
|
||
# 尝试从自由文本中提取关键信息
|
||
return extract_trading_signal_from_text(model_response)
|
||
|
||
except Exception as e:
|
||
retries += 1
|
||
if retries <= max_retries:
|
||
print(f"调用大模型API出错: {e}")
|
||
print(f"将在3秒后重试...")
|
||
time.sleep(3) # 等待3秒后重试
|
||
else:
|
||
print(f"已达到最大重试次数 ({max_retries}),调用大模型API失败")
|
||
print(f"错误详情: {e}")
|
||
print("\n请检查以下几点:")
|
||
print("1. API密钥格式是否正确,应以'sk-'开头")
|
||
print("2. 确认已安装最新版本的openai库: pip install --upgrade openai")
|
||
print("3. 确认您的API密钥对应的模型是否为deepseek-reasoner")
|
||
# 返回默认的不操作信号
|
||
return {"action": "不操作", "reason": f"API调用失败: {str(e)[:100]}", "confidence": 0, "stop_loss": 0, "take_profit": 0}
|
||
|
||
# 如果所有重试都失败了,返回默认值
|
||
return {"action": "不操作", "reason": "API调用重试耗尽", "confidence": 0, "stop_loss": 0, "take_profit": 0}
|
||
|
||
def parse_trading_signal(text):
|
||
"""
|
||
从文本格式的模型响应中解析出交易信号
|
||
|
||
Args:
|
||
text: 模型响应文本
|
||
|
||
Returns:
|
||
dict: 包含交易信号的字典
|
||
"""
|
||
lines = text.strip().split('\n')
|
||
trading_signal = {}
|
||
|
||
for line in lines:
|
||
if ':' in line:
|
||
key, value = line.split(':', 1)
|
||
key = key.strip().lower()
|
||
value = value.strip()
|
||
|
||
if key == 'action':
|
||
trading_signal['action'] = value
|
||
elif key == 'reason':
|
||
trading_signal['reason'] = value
|
||
elif key == 'confidence':
|
||
try:
|
||
trading_signal['confidence'] = int(value)
|
||
except ValueError:
|
||
# 尝试从文本中提取数字
|
||
import re
|
||
match = re.search(r'\d+', value)
|
||
if match:
|
||
trading_signal['confidence'] = int(match.group())
|
||
else:
|
||
trading_signal['confidence'] = 0
|
||
elif key == 'stop_loss':
|
||
try:
|
||
trading_signal['stop_loss'] = float(value)
|
||
except ValueError:
|
||
# 尝试从文本中提取数字
|
||
import re
|
||
match = re.search(r'\d+(\.\d+)?', value)
|
||
if match:
|
||
trading_signal['stop_loss'] = float(match.group())
|
||
else:
|
||
trading_signal['stop_loss'] = 0
|
||
elif key == 'take_profit':
|
||
try:
|
||
trading_signal['take_profit'] = float(value)
|
||
except ValueError:
|
||
# 尝试从文本中提取数字
|
||
import re
|
||
match = re.search(r'\d+(\.\d+)?', value)
|
||
if match:
|
||
trading_signal['take_profit'] = float(match.group())
|
||
else:
|
||
trading_signal['take_profit'] = 0
|
||
elif key == 'trailing_percent' or key == 'trailing_stop_percent':
|
||
try:
|
||
value_float = float(value)
|
||
# 确保值在合理范围内
|
||
if 0.0005 <= value_float <= 0.015:
|
||
trading_signal['trailing_percent'] = value_float
|
||
else:
|
||
# 如果值不在预期范围内,尝试判断是否使用了百分比格式
|
||
if value_float >= 0.05 and value_float <= 1.5:
|
||
# 可能是百分比格式,转换为小数
|
||
trading_signal['trailing_percent'] = value_float / 100
|
||
else:
|
||
# 设置为默认值
|
||
trading_signal['trailing_percent'] = 0.005
|
||
except ValueError:
|
||
# 尝试从文本中提取数字
|
||
import re
|
||
match = re.search(r'\d+(\.\d+)?', value)
|
||
if match:
|
||
try:
|
||
trailing_value = float(match.group())
|
||
if trailing_value >= 0.5 and trailing_value <= 10:
|
||
trading_signal['trailing_percent'] = trailing_value / 100
|
||
else:
|
||
trading_signal['trailing_percent'] = trailing_value
|
||
except:
|
||
trading_signal['trailing_percent'] = 0.02
|
||
else:
|
||
trading_signal['trailing_percent'] = 0.02
|
||
|
||
# 检查是否有缺失的字段,如果有,设置默认值
|
||
if 'action' not in trading_signal:
|
||
trading_signal['action'] = '不操作'
|
||
if 'reason' not in trading_signal:
|
||
trading_signal['reason'] = '未提供理由'
|
||
if 'confidence' not in trading_signal:
|
||
trading_signal['confidence'] = 0
|
||
if 'stop_loss' not in trading_signal:
|
||
trading_signal['stop_loss'] = 0
|
||
if 'take_profit' not in trading_signal:
|
||
trading_signal['take_profit'] = 0
|
||
if 'trailing_percent' not in trading_signal:
|
||
trading_signal['trailing_percent'] = 0.005 # 修改默认值
|
||
|
||
return trading_signal
|
||
|
||
def extract_trading_signal_from_text(text):
|
||
"""
|
||
从自由格式文本中尝试提取交易信号
|
||
|
||
Args:
|
||
text: 模型响应文本
|
||
|
||
Returns:
|
||
dict: 包含交易信号的字典
|
||
"""
|
||
# 默认交易信号
|
||
trading_signal = {
|
||
"action": "不操作",
|
||
"reason": "无法解析模型响应",
|
||
"confidence": 0,
|
||
"stop_loss": 0,
|
||
"take_profit": 0,
|
||
"trailing_percent": 0.005 # 更新默认的跟踪止损百分比
|
||
}
|
||
|
||
# 尝试判断交易方向
|
||
text_lower = text.lower()
|
||
if "开多" in text_lower:
|
||
trading_signal["action"] = "开多"
|
||
elif "开空" in text_lower:
|
||
trading_signal["action"] = "开空"
|
||
elif "平多" in text_lower:
|
||
trading_signal["action"] = "平多"
|
||
elif "平空" in text_lower:
|
||
trading_signal["action"] = "平空"
|
||
elif "不操作" in text_lower or "观望" in text_lower:
|
||
trading_signal["action"] = "不操作"
|
||
|
||
# 尝试从文本中提取置信度
|
||
import re
|
||
confidence_matches = re.findall(r'置信度[::]\s*(\d+)', text_lower)
|
||
if confidence_matches:
|
||
try:
|
||
trading_signal["confidence"] = int(confidence_matches[0])
|
||
except ValueError:
|
||
pass
|
||
|
||
# 尝试提取止损价
|
||
stop_loss_matches = re.findall(r'止损价[::]\s*(\d+(\.\d+)?)', text_lower)
|
||
if stop_loss_matches:
|
||
try:
|
||
trading_signal["stop_loss"] = float(stop_loss_matches[0][0])
|
||
except ValueError:
|
||
pass
|
||
|
||
# 尝试提取止盈价
|
||
take_profit_matches = re.findall(r'止盈价[::]\s*(\d+(\.\d+)?)', text_lower)
|
||
if take_profit_matches:
|
||
try:
|
||
trading_signal["take_profit"] = float(take_profit_matches[0][0])
|
||
except ValueError:
|
||
pass
|
||
|
||
# 尝试提取跟踪止损百分比
|
||
trailing_matches = re.findall(r'跟踪止损百分比[::]\s*(\d+(\.\d+)?)', text_lower)
|
||
if not trailing_matches:
|
||
trailing_matches = re.findall(r'跟踪百分比[::]\s*(\d+(\.\d+)?)', text_lower)
|
||
|
||
if trailing_matches:
|
||
try:
|
||
trailing_value = float(trailing_matches[0][0])
|
||
# 判断是否为百分比格式
|
||
if trailing_value >= 0.5 and trailing_value <= 10:
|
||
trading_signal["trailing_percent"] = trailing_value / 100
|
||
else:
|
||
trading_signal["trailing_percent"] = trailing_value
|
||
except ValueError:
|
||
pass
|
||
|
||
# 提取理由
|
||
reason_matches = re.findall(r'理由[::]\s*(.*?)(?=\n|$)', text_lower)
|
||
if reason_matches:
|
||
trading_signal["reason"] = reason_matches[0]
|
||
|
||
return trading_signal
|
||
|
||
# 修改run_trader函数,不再传递llm_config参数
|
||
def run_trader(broker_id, td_server, investor_id, password, app_id, auth_code, md_queue=None, page_dir='', private_resume_type=2, public_resume_type=2, load_history=False, history_rows=1000,trader_rows=10):
|
||
my_trader = MyTrader(
|
||
broker_id,
|
||
td_server,
|
||
investor_id,
|
||
password,
|
||
app_id,
|
||
auth_code,
|
||
md_queue,
|
||
page_dir,
|
||
private_resume_type,
|
||
public_resume_type
|
||
)
|
||
|
||
# 设置历史数据加载参数
|
||
my_trader.load_history = load_history
|
||
my_trader.history_rows = history_rows
|
||
my_trader.trader_rows = trader_rows
|
||
my_trader.Join()
|
||
|
||
# 修改主函数中的配置和调用方式
|
||
if __name__ == '__main__':
|
||
|
||
#公众号:松鼠Quant
|
||
#主页:www.quant789.com
|
||
#本策略仅作学习交流使用,实盘交易盈亏投资者个人负责!!!
|
||
#版权归松鼠Quant所有,禁止转发、转卖源码违者必究。
|
||
|
||
#注意:运行前请先安装好algoplus,
|
||
# pip install AlgoPlus
|
||
#http://www.algo.plus/ctp/python/0103001.html
|
||
|
||
|
||
# 配置大模型参数 - 直接通过环境变量设置
|
||
import os
|
||
# 设置您的实际API密钥
|
||
api_key = "" # 请确保使用有效的密钥
|
||
os.environ["OPENAI_API_KEY"] = api_key
|
||
|
||
# 同时设置到全局变量中
|
||
GLOBAL_LLM_CONFIG['api_key'] = api_key
|
||
GLOBAL_LLM_CONFIG['base_url'] = "https://api.deepseek.com"
|
||
GLOBAL_LLM_CONFIG['model_name'] = "deepseek-reasoner"
|
||
# 历史数据加载配置
|
||
LOAD_HISTORY = False # 是否加载历史数据
|
||
HISTORY_ROWS = 30 # 加载历史文件中数据量
|
||
TRADER_ROWS = 10 # 当tader_df里的数据大于10行时开始计算指标及触发AI模型
|
||
# 设置K线时间粒度
|
||
BAR_RESAMPLE_RULE = '1T' # 1分钟K线,可以修改为'5T'(5分钟),'15T'(15分钟)等,也可用'5S'(5秒),'30S'(30秒)或'1H'(1小时),'2H'(2小时),'4H'(4小时),'1D'(1天)
|
||
|
||
# 测试API连接
|
||
print(f"测试API连接,使用密钥: {api_key[:5]}...{api_key[-5:]}")
|
||
try:
|
||
client = OpenAI(api_key=api_key, base_url="https://api.deepseek.com")
|
||
response = client.chat.completions.create(
|
||
model="deepseek-reasoner",
|
||
messages=[
|
||
{"role": "system", "content": "你是一个助手"},
|
||
{"role": "user", "content": "测试"}
|
||
],
|
||
stream=True, # 新增此行
|
||
max_tokens=10
|
||
)
|
||
print("API连接测试成功!")
|
||
except Exception as e:
|
||
print(f"API连接测试失败: {e}")
|
||
import traceback
|
||
traceback.print_exc()
|
||
|
||
#用simnow模拟,不要忘记屏蔽下方实盘的future_account字典
|
||
future_account = get_simulate_account(
|
||
investor_id='', # simnow账户,注意是登录账户的ID,SIMNOW个人首页查看
|
||
password='', # simnow密码
|
||
server_name='电信1', # 电信1、电信2、移动、TEST、N视界
|
||
subscribe_list=[b'au2506'], # 合约列表
|
||
)
|
||
|
||
# #实盘用这个,不要忘记屏蔽上方simnow的future_account字典
|
||
# future_account = FutureAccount(
|
||
# broker_id='', # 期货公司BrokerID
|
||
# server_dict={'TDServer': "ip:port", 'MDServer': 'ip:port'}, # TDServer为交易服务器,MDServer为行情服务器。服务器地址格式为"ip:port。"
|
||
# reserve_server_dict={}, # 备用服务器地址
|
||
# investor_id='', # 账户
|
||
# password='', # 密码
|
||
# app_id='simnow_client_test', # 认证使用AppID
|
||
# auth_code='0000000000000000', # 认证使用授权码
|
||
# subscribe_list=[b'rb2405'], # 订阅合约列表
|
||
# md_flow_path='./log', # MdApi流文件存储地址,默认MD_LOCATION
|
||
# td_flow_path='./log', # TraderApi流文件存储地址,默认TD_LOCATION
|
||
# )
|
||
|
||
print('开始',len(future_account.subscribe_list))
|
||
# 共享队列
|
||
share_queue = Queue(maxsize=200)
|
||
|
||
# 行情进程
|
||
md_process = Process(target=run_tick_engine, args=(future_account, [share_queue]))
|
||
|
||
# 交易进程 - 增加历史数据加载参数
|
||
trader_process = Process(target=run_trader, args=(
|
||
future_account.broker_id,
|
||
future_account.server_dict['TDServer'],
|
||
future_account.investor_id,
|
||
future_account.password,
|
||
future_account.app_id,
|
||
future_account.auth_code,
|
||
share_queue, # 队列
|
||
future_account.td_flow_path,
|
||
2, # private_resume_type
|
||
2, # public_resume_type
|
||
LOAD_HISTORY, # 传递历史数据加载参数
|
||
HISTORY_ROWS, # 传递历史数据行数参数
|
||
TRADER_ROWS, # 传递最低计算阈值
|
||
))
|
||
|
||
md_process.start()
|
||
trader_process.start()
|
||
|
||
md_process.join()
|
||
trader_process.join()
|
||
|
||
|