286 lines
8.7 KiB
Python
286 lines
8.7 KiB
Python
|
import ftplib
|
|||
|
import os
|
|||
|
from datetime import datetime
|
|||
|
from ftplib import FTP, error_perm
|
|||
|
from pathlib import Path
|
|||
|
|
|||
|
import akshare as ak
|
|||
|
import efinance as ef
|
|||
|
import qstock as qs
|
|||
|
import tushare as ts
|
|||
|
import pandas as pd
|
|||
|
import time
|
|||
|
import random
|
|||
|
from tabulate import tabulate
|
|||
|
import platform
|
|||
|
import xcsc_tushare as xc
|
|||
|
|
|||
|
token = '0718534658b9d91b3f03dc8b220e4062193ebf6f6414d036505165e1'
|
|||
|
|
|||
|
xc.set_token('bd4f26f1eca8d660bd23e229260df46002d630d6e1fb9226380edec8')
|
|||
|
# 仿真环境用这个方式可以连上,生产环境用这个连不上
|
|||
|
xcsc_pro = xc.pro_api(env='prd', server='http://116.128.206.39:7172')
|
|||
|
|
|||
|
# 显示所有列
|
|||
|
pd.set_option('display.max_columns', None)
|
|||
|
# 显示所有行
|
|||
|
pd.set_option('display.max_rows', None)
|
|||
|
# 输出不折行
|
|||
|
pd.set_option('expand_frame_repr', False)
|
|||
|
# 最大列宽度
|
|||
|
pd.set_option('display.max_colwidth', None)
|
|||
|
|
|||
|
|
|||
|
def sleep():
|
|||
|
print("开始休眠,防止ip被拉黑或者进小黑屋")
|
|||
|
# 生成随机的休眠时间
|
|||
|
sleep_time = random.uniform(0, 2)
|
|||
|
# 执行休眠
|
|||
|
time.sleep(sleep_time)
|
|||
|
print("休眠结束!")
|
|||
|
|
|||
|
|
|||
|
def print_markdown(df: pd):
|
|||
|
print(tabulate(df, headers='keys', tablefmt='github'))
|
|||
|
|
|||
|
|
|||
|
def get_file(size):
|
|||
|
"""
|
|||
|
获取不同系统下的文件路径
|
|||
|
:param size:
|
|||
|
:return:
|
|||
|
"""
|
|||
|
sys_platform = platform.platform().lower()
|
|||
|
if "macos" in sys_platform:
|
|||
|
return f'/Users/renmeng/work_space/python_work/qnloft-get-web-everything/股票金融/量化交易/股票数据/{size}'
|
|||
|
elif "windows" in sys_platform:
|
|||
|
return f'E:\\Python_Workplace\qnloft-get-web-everything\\股票金融\\量化交易\\股票数据\\{size}'
|
|||
|
else:
|
|||
|
print("其他系统")
|
|||
|
|
|||
|
|
|||
|
def write_file(content, file_name, mode_type='a'):
|
|||
|
file = ""
|
|||
|
sys_platform = platform.platform().lower()
|
|||
|
if "macos" in sys_platform:
|
|||
|
file = f'/Users/renmeng/work_space/python_work/qnloft-get-web-everything/股票金融/量化交易/股票数据/{file_name}'
|
|||
|
elif "windows" in sys_platform:
|
|||
|
file = f'D:\\文档\\数据测试\\{file_name}'
|
|||
|
else:
|
|||
|
print("其他系统")
|
|||
|
path = Path(file)
|
|||
|
# 创建目录或文件
|
|||
|
if not path.exists():
|
|||
|
if path.is_dir():
|
|||
|
path.mkdir(parents=True) # 创建目录及其父目录
|
|||
|
else:
|
|||
|
path.touch() # 创建文件
|
|||
|
with path.open(mode=mode_type) as file:
|
|||
|
file.write(content)
|
|||
|
|
|||
|
|
|||
|
def create_file(file_name):
|
|||
|
path = Path(file_name)
|
|||
|
# 创建目录或文件
|
|||
|
if not path.exists():
|
|||
|
path.mkdir(parents=True) # 创建目录及其父目录
|
|||
|
|
|||
|
|
|||
|
def get_random_stock(size, n=0):
|
|||
|
if size == 'M':
|
|||
|
s_list = Path(get_file(size)).read_text().splitlines()
|
|||
|
elif size == 'S':
|
|||
|
s_list = Path(get_file(size)).read_text().splitlines()
|
|||
|
elif size == 'L':
|
|||
|
s_list = Path(get_file(size)).read_text().splitlines()
|
|||
|
else:
|
|||
|
raise ValueError("size输入错误!")
|
|||
|
if n > 0:
|
|||
|
return random.sample(s_list, n)
|
|||
|
else:
|
|||
|
return s_list
|
|||
|
|
|||
|
|
|||
|
def del_file_lines(size, to_delete):
|
|||
|
content = Path(get_file(size)).read_text()
|
|||
|
# 删除匹配的数字
|
|||
|
for char in to_delete:
|
|||
|
content = content.replace(char, '')
|
|||
|
|
|||
|
# 将更新后的内容写回文件
|
|||
|
with Path(get_file(size)).open(mode='w') as file:
|
|||
|
file.writelines(content)
|
|||
|
|
|||
|
|
|||
|
def if_run(current_date=datetime.now()):
|
|||
|
"""
|
|||
|
判断当前日期是否是交易日
|
|||
|
:param current_date:
|
|||
|
:return:
|
|||
|
"""
|
|||
|
df = ak.tool_trade_date_hist_sina()
|
|||
|
# 将日期列转换为 datetime 类型
|
|||
|
df['trade_date'] = pd.to_datetime(df['trade_date'])
|
|||
|
date_now_hour = current_date.hour
|
|||
|
# 将日期格式化为 "yyyy-mm-dd" 形式
|
|||
|
formatted_date = current_date.strftime('%Y-%m-%d')
|
|||
|
# 检查 DataFrame 是否包含特定日期
|
|||
|
contains_target_date = (df['trade_date'] == formatted_date).any()
|
|||
|
print(f'当日日期是:{formatted_date} ,今日是否开盘:{contains_target_date}')
|
|||
|
if contains_target_date:
|
|||
|
return True
|
|||
|
return False
|
|||
|
|
|||
|
|
|||
|
def return_trading_day(now_date, offset_date, format_type='%Y%m%d'):
|
|||
|
"""
|
|||
|
获取当前时间的 前n个交易日期,后n个交易日期
|
|||
|
:param format_type:
|
|||
|
:param offset_date:
|
|||
|
:param now_date:
|
|||
|
:return:
|
|||
|
"""
|
|||
|
df = ak.tool_trade_date_hist_sina()
|
|||
|
# 将日期列转换为 datetime 类型
|
|||
|
df['trade_date'] = pd.to_datetime(df['trade_date'])
|
|||
|
if isinstance(now_date, datetime):
|
|||
|
formatted_date = now_date.strftime('%Y-%m-%d')
|
|||
|
else:
|
|||
|
formatted_date = now_date
|
|||
|
selected_indexes = df[df['trade_date'] == formatted_date].index
|
|||
|
|
|||
|
return df.loc[selected_indexes + offset_date, 'trade_date'].dt.strftime(format_type).values[0]
|
|||
|
|
|||
|
|
|||
|
def upload_files_to_ftp(local_directory, remote_directory, file_name=None):
|
|||
|
"""
|
|||
|
ftp上传
|
|||
|
:param local_directory:
|
|||
|
:param remote_directory:
|
|||
|
:param file_name:
|
|||
|
:return:
|
|||
|
"""
|
|||
|
try:
|
|||
|
ftp = FTP("qxu1142200198.my3w.com")
|
|||
|
ftp.login('qxu1142200198', '48XZ55MB')
|
|||
|
except error_perm as e:
|
|||
|
print(f"Network error: {e}")
|
|||
|
return False
|
|||
|
try:
|
|||
|
# 切换目录
|
|||
|
ftp.cwd(remote_directory)
|
|||
|
except ftplib.error_perm:
|
|||
|
print("目录不存在, 开始创建...")
|
|||
|
ftp.mkd(remote_directory)
|
|||
|
try:
|
|||
|
path = Path(local_directory)
|
|||
|
# 判断是文件夹还是文件
|
|||
|
if path.is_dir() and file_name is None:
|
|||
|
files = os.listdir(local_directory) # get list of files in local directory
|
|||
|
for file in files:
|
|||
|
if file.endswith(".html"): # upload only .txt files
|
|||
|
local_file = f"{local_directory}/{file}"
|
|||
|
remote_file = f"{remote_directory}/{file}"
|
|||
|
print(local_file, "----->>", remote_file)
|
|||
|
with open(local_file, 'rb') as f:
|
|||
|
try:
|
|||
|
ftp.delete(remote_file)
|
|||
|
except ftplib.error_perm:
|
|||
|
print("目录不存在, 或已经删除...")
|
|||
|
ftp.storbinary(f"STOR {remote_file}", f) # upload file to FTP server
|
|||
|
if file_name is not None and len(file_name) > 0:
|
|||
|
local_file = f"{local_directory}/{file_name}"
|
|||
|
remote_file = f"{remote_directory}/{file_name}"
|
|||
|
print(local_file, "----->>", remote_file)
|
|||
|
with open(local_file, 'rb') as f:
|
|||
|
try:
|
|||
|
ftp.delete(remote_file)
|
|||
|
except ftplib.error_perm:
|
|||
|
print("目录不存在, 或已经删除...")
|
|||
|
ftp.storbinary(f"STOR {remote_file}", f) # upload file to FTP server
|
|||
|
finally:
|
|||
|
ftp.quit()
|
|||
|
return True
|
|||
|
|
|||
|
|
|||
|
def back_testing(df, cond: str, n=3):
|
|||
|
"""
|
|||
|
回撤测试
|
|||
|
:param df:
|
|||
|
:param cond:
|
|||
|
:param n:
|
|||
|
:return:
|
|||
|
"""
|
|||
|
filtered_df = df.eval(cond)
|
|||
|
total, tomorrow_rise, tomorrow_fall = 0, 0, 0
|
|||
|
res_df = pd.DataFrame(
|
|||
|
columns=['code', '日期', '明天涨跌幅', 'n日最大涨幅', 'n日平均涨跌幅', 'n日最大回撤'])
|
|||
|
for index, row in df.iterrows():
|
|||
|
if filtered_df[index] and index > n:
|
|||
|
# print(f'{row["trade_date"]} --> {row["KDJ_D"]} --> {row["KDJ_J"]}')
|
|||
|
total += 1
|
|||
|
# 明天涨跌幅情况
|
|||
|
tomorrow_pre = df.iloc[-index:-index + 1]['pct_chg'].values[0].round(2)
|
|||
|
# n日 最大涨幅
|
|||
|
max_h = df.iloc[-index:-index + n]['pct_chg'].max().round(2)
|
|||
|
# n日 平均涨跌幅
|
|||
|
mean_h = df.iloc[-index:-index + n]['pct_chg'].mean().round(2)
|
|||
|
# n日 最大回撤
|
|||
|
h_max = df.iloc[-index:-index + n]['high'].max()
|
|||
|
l_min = df.iloc[-index:-index + n]['low'].min()
|
|||
|
max_ret = f"{(h_max - l_min) / l_min:.2f}"
|
|||
|
res_df.loc[total, 'code'], res_df.loc[total, '日期'] = row['ts_code'], row["trade_date"]
|
|||
|
res_df.loc[total, '明天涨跌幅'] = tomorrow_pre
|
|||
|
res_df.loc[total, 'n日最大涨幅'] = max_h
|
|||
|
res_df.loc[total, 'n日平均涨跌幅'] = mean_h
|
|||
|
res_df.loc[total, 'n日最大回撤'] = max_ret
|
|||
|
return res_df
|
|||
|
|
|||
|
|
|||
|
def buying_and_selling_decisions(df, cond_buy: str, cond_sell: str, cond_buy_read=None, cond_sell_read=None):
|
|||
|
"""
|
|||
|
买卖测试
|
|||
|
:param df:
|
|||
|
:param cond_buy:
|
|||
|
:param cond_sell:
|
|||
|
:param cond_buy_read:
|
|||
|
:param cond_sell_read:
|
|||
|
:return:
|
|||
|
"""
|
|||
|
total, buy, sell, total_chg, buy_date, flag_ready_b, flag_ready_s = 0, 0, 0, 0, 0, False, False
|
|||
|
res_df = pd.DataFrame(
|
|||
|
columns=['code', '买入日期', '卖出日期', '盈利', '持仓天数'])
|
|||
|
for index, row in df.iterrows():
|
|||
|
trade_date, c = row["trade_date"], row['close']
|
|||
|
if (eval(cond_buy_read) or cond_buy_read is None) and buy == 0:
|
|||
|
# 准备买入
|
|||
|
print(f"{trade_date} {cond_buy_read},准备买入!")
|
|||
|
flag_ready_b = True
|
|||
|
if eval(cond_buy) and buy == 0 and flag_ready_b:
|
|||
|
# 买入
|
|||
|
print(f"{trade_date} {cond_buy},买入操作!")
|
|||
|
buy, buy_date = c, trade_date
|
|||
|
flag_ready_b = False
|
|||
|
if (eval(cond_sell_read) or cond_sell_read is None) and buy > 0:
|
|||
|
# 准备卖出
|
|||
|
print(f"{trade_date} {cond_sell_read},准备卖出!")
|
|||
|
flag_ready_s = True
|
|||
|
if eval(cond_sell) and buy > 0 and flag_ready_s:
|
|||
|
print(f"{trade_date} {cond_sell},卖出操作!")
|
|||
|
total += 1
|
|||
|
# 卖出
|
|||
|
sell = c
|
|||
|
# 计算盈利
|
|||
|
chg = round(((sell - buy) / buy) * 100, 2)
|
|||
|
# 计算持仓天数
|
|||
|
start_date = datetime.strptime(buy_date, "%Y%m%d")
|
|||
|
end_date = datetime.strptime(trade_date, "%Y%m%d")
|
|||
|
# 计算间隔天数
|
|||
|
interval_days = (end_date - start_date).days
|
|||
|
buy, flag_ready_s = 0, False
|
|||
|
res_df.loc[total, 'code'], res_df.loc[total, '买入日期'] = row['ts_code'], buy_date
|
|||
|
res_df.loc[total, '卖出日期'] = trade_date
|
|||
|
res_df.loc[total, '盈利'] = chg
|
|||
|
res_df.loc[total, '持仓天数'] = interval_days
|
|||
|
return res_df
|