Files
Quant_Code/999.账户相关/simnow_trader/traderdata/0323/app.py
2025-04-09 17:18:30 +08:00

345 lines
13 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
from flask import Flask, render_template, jsonify, make_response
from flask_socketio import SocketIO
import pandas as pd
import numpy as np
import os
import ast
import time
from datetime import datetime
import requests
# 加入邮件通知
import smtplib
from email.mime.text import MIMEText # 导入 MIMEText 类发送纯文本邮件
from email.mime.multipart import (
MIMEMultipart,
)
# import akshare as ak
app = Flask(__name__)
app.config['SECRET_KEY'] = 'secret!'
socketio = SocketIO(app)
# 添加安全响应头
@app.after_request
def add_security_headers(response):
response.headers['X-Content-Type-Options'] = 'nosniff'
response.headers['Cache-Control'] = 'no-store, no-cache, must-revalidate, max-age=0'
response.headers['Pragma'] = 'no-cache'
response.headers['Expires'] = '0'
return response
# from email.mime.application import MIMEApplication
# 配置邮件信息
receivers = ["240884432@qq.com"] # 设置邮件接收人地址
# subject = "TD_Simnow_Signal" # 设置邮件主题 订单流策略交易信号
# 配置邮件服务器信息
smtp_server = "smtp.qq.com" # 设置发送邮件的 SMTP 服务器地址
smtp_port = 465 # 设置发送邮件的 SMTP 服务器端口号,一般为 25 端口 465
sender = "240884432@qq.com" # 设置发送邮件的邮箱地址
username = "240884432@qq.com" # 设置发送邮件的邮箱用户名
password = "osjyjmbqrzxtbjbf" # zrmpcgttataabhjh设置发送邮件的邮箱密码或授权码
last_sent_time = 0
count = 0
time_period = 30
delta_sum_trend=0
delta_trend=0
dj_trend = 0
delta_rate = 0.8
dj_rate = 0.8
# 获取当前工作目录
current_directory = os.getcwd()
print("当前工作目录:", current_directory)
# 设置新的工作目录
new_directory = r"C:/simnow_trader/traderdata"
os.chdir(new_directory)
# 验证新的工作目录
updated_directory = os.getcwd()
print("已更改为新的工作目录:", updated_directory)
# 获取当前文件夹中所有包含"ofdata"字符的CSV文件
def get_csv_files():
files = {}
for filename in os.listdir():
if "ofdata" in filename and filename.endswith(".csv"):
files[filename] = os.path.join(os.getcwd(), filename)
return files
def send_mail(subject, text):
global last_sent_time, count
# 检查时间间隔
current_time = time.time()
print('count:',count)
if count == 1 and current_time - last_sent_time <1:
print("current_time:",current_time)
print("last_sent_time:",last_sent_time)
print("一分钟内已发送过邮件,本次跳过")
return
elif count ==1 and current_time - last_sent_time >1:
count = 0
if count == 0 and current_time - last_sent_time < 1:
msg = MIMEMultipart()
msg["From"] = sender
msg["To"] = ";".join(receivers)
msg["Subject"] = subject
html_content = f"""
<html>
<body>
<p>以下是数据的最后一列:</p>
{text}
</body>
</html>
"""
msg.attach(MIMEText(html_content, 'html'))
smtp = smtplib.SMTP_SSL(smtp_server, smtp_port)
smtp.login(username, password)
smtp.sendmail(sender, receivers, msg.as_string())
count = 1
smtp.quit()
# 根据文件路径加载数据只读取前12列
def load_data(file_path):
df = pd.read_csv(file_path, usecols=range(12)).iloc[-1200:] # 只读取前12列
df = df.drop_duplicates(subset='datetime', keep='first').reset_index(drop=True)
# df = df[df['high'] != df['low']]
df["delta"] = df["delta"].astype(float)
df['datetime'] = pd.to_datetime(df['datetime'],format='ISO8601')#, dayfirst=True, format='mixed'
# df['delta累计'] = df.groupby(df['datetime'].dt.date)['delta'].cumsum()
# 自定义分组逻辑前一日21:00至当日15:00为一天
def get_trading_day(dt):
# 如果时间在21:00之后属于下一个交易日
if dt.hour >= 21:
return (dt + pd.Timedelta(days=1)).date()
# 如果时间在15:00之前属于当前交易日
elif dt.hour < 15:
return dt.date()
# 15:00-21:00之间的数据属于当前交易日
else:
return dt.date()
# 添加交易日列并转换为字符串
df['trading_day'] = df['datetime'].apply(get_trading_day)
df['trading_day'] = df['trading_day'].astype(str) # 将日期转换为字符串
# 按交易日计算delta累计
df['delta累计'] = df.groupby('trading_day')['delta'].cumsum()
df = df.fillna('缺值')
df['终极平滑值'],df['趋势方向'] = ultimate_smoother(df['close'],time_period)
df['datetime'] = df['datetime'].dt.strftime("%Y-%m-%d %H:%M:%S")
df['POC'] = add_poc_column(df)
df['最终趋势'] = finall_trend(df['delta累计'],df['趋势方向'])
# print(df.tail(1))
# print(type(df['delta累计'].iloc[-1]))
def send_feishu_message(text):
headers = {
"Content-Type": "application/json"
}
table_html = f'<table border="1" class="dataframe">\n <thead>\n <tr style="text-align: right;">\n <th>symbol</th>\n <th>datetime</th>\n <th>delta</th>\n <th>close</th>\n <th>open</th>\n <th>high</th>\n <th>low</th>\n <th>volume</th>\n <th>dj</th>\n <th>trading_day</th>\n <th>delta累计</th>\n <th>终极平滑值</th>\n <th>趋势方向</th>\n <th>POC</th>\n <th>最终趋势</th>\n </tr>\n </thead>\n <tbody>\n{text}\n </tbody>\n</table>'
data = {
"msg_type": "text",
"content": {
"text": table_html
}
}
response = requests.post("https://open.feishu.cn/open-apis/bot/v2/hook/8608dfa4-e599-462a-8dba-6ac72873dd27", headers=headers, json=data)
if response.status_code != 200:
print(f"飞书消息发送失败,状态码: {response.status_code}, 响应内容: {response.text}")
if df['delta累计'].iloc[-2] < 0 and df['delta累计'].iloc[-1] > 0 and df['趋势方向'].iloc[-1] == '多头趋势':
table_text = df.iloc[:,3:].tail(1).to_html(index=False)
send_feishu_message("日内delta累计多头信号\n" + table_text)
elif df['delta累计'].iloc[-2] > 0 and df['delta累计'].iloc[-1] < 0 and df['趋势方向'].iloc[-1] == '空头趋势':
table_text = df.iloc[:,3:].tail(1).to_html(index=False)
send_feishu_message("日内delta累计空头信号\n" + table_text)
else:
pass
# djValues[i] >= maxDJ * 0.8 && ultimateValues[i] > ma120[i]
if df['dj'].iloc[-1] >= 0.8 * max(df['dj'].iloc[-121:-1] ) and df['趋势方向'].iloc[-1] == '多头趋势' :
table_text = df.iloc[:,3:].tail(1).to_html(index=False)
# send_mail("dj多头信号",table_text)
send_feishu_message("dj多头信号\n" + table_text)
elif df['dj'].iloc[-1] <= 0.8 * min(df['dj'].iloc[-121:-1] ) and df['趋势方向'].iloc[-1] == '空头趋势' :
table_text = df.iloc[:,3:].tail(1).to_html(index=False)
# send_mail("dj空头信号",table_text)
send_feishu_message("dj空头信号\n" + table_text)
else:
pass
# deltaValues[i] >= maxDelta * 0.8 && ultimateValues[i] > ma120[i])
if df['delta'].iloc[-1] >= 0.8 * max(df['delta'].iloc[-121:-1] ) and df['趋势方向'].iloc[-1] == '多头趋势' :
table_text = df.iloc[:,3:].tail(1).to_html(index=False)
# send_mail("delta多头信号",table_text)
send_feishu_message("delta多头信号\n" + table_text)
elif df['delta'].iloc[-1] <= 0.8 * min(df['delta'].iloc[-121:-1] ) and df['趋势方向'].iloc[-1] == '空头趋势' :
table_text = df.iloc[:,3:].tail(1).to_html(index=False)
# send_mail("delta空头信号",table_text)
send_feishu_message("delta空头信号\n" + table_text)
else:
pass
return df.to_dict(orient="records")#.iloc[-48:]
# return df.iloc[-60:].iloc[::-1].to_dict(orient="records")
def finall_trend(delta_sum,trend):
f_trend = [None]*(len(delta_sum))
# delta_sum = delta_sum.astype(float)
for i in range(len(delta_sum)):
if (delta_sum[i] == '缺值') or (trend[i] == '缺值'):
f_trend[i] = '方向不明'
# return f_trend
else:
if delta_sum[i] > 0 and (trend[i] == '多头趋势'):
f_trend[i] = '强多头'
elif delta_sum[i] < 0 and (trend[i] == '空头趋势'):
f_trend[i] = '强空头'
else:
f_trend[i] = '方向不明'
return f_trend
def safe_literal_eval(x):
"""带异常处理的安全转换"""
try:
return ast.literal_eval(x)
except ValueError:
return [] # 返回空列表作为占位符
def add_poc_column(df):
# 安全转换列数据
df['price'] = df['price'].apply(safe_literal_eval)
df['Ask'] = df['Ask'].apply(lambda x: list(map(int, safe_literal_eval(x))))
df['Bid'] = df['Bid'].apply(lambda x: list(map(int, safe_literal_eval(x))))
# 定义处理函数(带数据验证)
def find_poc(row):
# 验证三个列表长度一致且非空
if not (len(row['price']) == len(row['Ask']) == len(row['Bid']) > 0):
return '缺值' # 返回空值标记异常数据
sums = [a + b for a, b in zip(row['Ask'], row['Bid'])]
try:
max_index = sums.index(max(sums))
return row['price'][max_index]
except ValueError:
return '缺值' # 处理空求和列表情况
# 应用处理函数
df['POC'] = df.apply(find_poc, axis=1)
# 可选:统计异常数据
error_count = df['POC'].isnull().sum()
if error_count > 0:
print(f"警告:发现 {error_count} 行异常数据已标记为NaN")
return df['POC']
def ultimate_smoother(price,period):
# 初始化变量(修正角度单位为弧度)
a1 = np.exp(-1.414 * np.pi / period)
b1 = 2 * a1 * np.cos(1.414 * np.pi / period) # 将180改为np.pi
c2 = b1
c3 = -a1 ** 2
c1 = (1 + c2 - c3) / 4
# 准备输出序列
us = np.zeros(len(price))
us_new = np.zeros(len(price))
trend = [None]*(len(price))
ma_close = np.zeros(len(price))
# 前4个点用原始价格初始化
for i in range(len(price)):
if i < 4:
us[i] = price.iloc[i]
else:
# 应用递归公式
us[i] = (1 - c1) * price.iloc[i] + (2 * c1 - c2) * price.iloc[i-1] \
- (c1 + c3) * price.iloc[i-2] + c2 * us[i-1] + c3 * us[i-2]
us_new = np.around(us, decimals=2)
ma_close = price.rolling(window=4*period).mean()#5*
# if us_new[i]>price[i] and ma_close[i]>price[i]:
# trend[i] = '空头趋势'
# elif us_new[i]<price[i] and ma_close[i]<price[i]:
# trend[i] = '多头趋势'
# else:
# trend[i] = '无趋势'
if us_new[i] < ma_close.iloc[i]:
trend[i] = '空头趋势'
elif us_new[i] > ma_close.iloc[i]:
trend[i] = '多头趋势'
else:
trend[i] = '无趋势'
return us_new,trend
@app.route("/")
def index():
return render_template("index.html")
@app.route("/kline")
def kline():
return render_template("kline.html")
@app.route("/api/data")
def get_data():
try:
files = get_csv_files()
data = {}
for symbol, filename in files.items():
loaded_data = load_data(filename)
if loaded_data:
data[symbol] = loaded_data
return jsonify(data)
except Exception as e:
return jsonify({"error": str(e)})
def should_update():
"""检查是否应该在当前时间更新数据"""
now = datetime.now()
# 检查是否是整点5分钟
if now.minute % 2 == 0:
# 检查是否在5秒内
if now.second < 2:
return True
return False
def background_thread():
"""后台线程在每整点5分钟的5秒内发送数据更新"""
while True:
if should_update():
files = get_csv_files()
data = {}
for file_name, file_path in files.items():
data[file_name] = load_data(file_path)
socketio.emit('data_update', data)
print(f"数据更新完成 - {datetime.now().strftime('%Y-%m-%d %H:%M:%S')}")
time.sleep(1) # 每秒检查一次
@socketio.on('connect')
def handle_connect():
print('Client connected')
# 启动后台线程
socketio.start_background_task(background_thread)
@socketio.on('disconnect')
def handle_disconnect():
print('Client disconnected')
if __name__ == "__main__":
socketio.run(app, host='0.0.0.0', port=5000, debug=True) # 监听所有网络接口