提交基础内容

This commit is contained in:
2025-03-12 19:54:53 +08:00
commit 5dfb8d7b1a
9 changed files with 309 additions and 0 deletions

8
.idea/.gitignore generated vendored Normal file
View File

@@ -0,0 +1,8 @@
# 默认忽略的文件
/shelf/
/workspace.xml
# 基于编辑器的 HTTP 客户端请求
/httpRequests/
# Datasource local storage ignored files
/dataSources/
/dataSources.local.xml

View File

@@ -0,0 +1,19 @@
<component name="InspectionProjectProfileManager">
<profile version="1.0">
<option name="myName" value="Project Default" />
<inspection_tool class="DuplicatedCode" enabled="true" level="WEAK WARNING" enabled_by_default="true">
<Languages>
<language minSize="74" name="Python" />
</Languages>
</inspection_tool>
<inspection_tool class="PyPackageRequirementsInspection" enabled="true" level="WARNING" enabled_by_default="true">
<option name="ignoredPackages">
<value>
<list size="1">
<item index="0" class="java.lang.String" itemvalue="pandas" />
</list>
</value>
</option>
</inspection_tool>
</profile>
</component>

View File

@@ -0,0 +1,6 @@
<component name="InspectionProjectProfileManager">
<settings>
<option name="USE_PROJECT_PROFILE" value="false" />
<version value="1.0" />
</settings>
</component>

7
.idea/misc.xml generated Normal file
View File

@@ -0,0 +1,7 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="Black">
<option name="sdkName" value="Python 3.10 (土信号)" />
</component>
<component name="ProjectRootManager" version="2" project-jdk-name="Python 3.10 (土信号)" project-jdk-type="Python SDK" />
</project>

8
.idea/modules.xml generated Normal file
View File

@@ -0,0 +1,8 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="ProjectModuleManager">
<modules>
<module fileurl="file://$PROJECT_DIR$/.idea/土信号.iml" filepath="$PROJECT_DIR$/.idea/土信号.iml" />
</modules>
</component>
</project>

10
.idea/土信号.iml generated Normal file
View File

@@ -0,0 +1,10 @@
<?xml version="1.0" encoding="UTF-8"?>
<module type="PYTHON_MODULE" version="4">
<component name="NewModuleRootManager">
<content url="file://$MODULE_DIR$">
<excludeFolder url="file://$MODULE_DIR$/venv" />
</content>
<orderEntry type="inheritedJdk" />
<orderEntry type="sourceFolder" forTests="false" />
</component>
</module>

41
土-优化01策略.txt Normal file
View File

@@ -0,0 +1,41 @@
X_1:=LLV(LOW,10);
X_2:=HHV(HIGH,25);
X_5:=3.5;
X_6:=REF(EMA((CLOSE-X_1)/(X_2-X_1)*4,4),1);
X_7:=IF(EXIST(CROSS(X_6,X_5),5),0,1);
X_10:=(REF(CLOSE,1)-REF(CLOSE,2))/REF(CLOSE,2)*100;
X_11:=2*(REF(CLOSE+HIGH+LOW,1)+OPEN)*100;
X_12:=(X_11/EMA(X_11,4)-1)*100;
X_13:=X_7*X_12;
X_14:=DMA(EMA(OPEN,12),SUM(REF(VOL,1),5)/3/CAPITAL);
X_15:=X_7*(OPEN-X_14)/X_14*200;
X_16:=0-(HHV(EMA(OPEN,5),14)-OPEN)/OPEN*5000;
X_17:=REF(CLOSE+LOW+3*OPEN+HIGH,1)/6;
X_18:=20*X_17+19*REF(X_17,1)+18*REF(X_17,2)+17*REF(X_17,3)+16*REF(X_17,4)+15*REF(X_17,5)+14*REF(X_17,6)+13*REF(X_17,7)+12*REF(X_17,8)+11*REF(X_17,9);
X_19:=10*REF(X_17,10)+9*REF(X_17,11)+8*REF(X_17,12)+7*REF(X_17,13)+6*REF(X_17,14)+5*REF(X_17,15)+4*REF(X_17,16)+3*REF(X_17,17)+2*REF(X_17,18)+REF(X_17,20);
X_20:=(X_18+X_19)/210;
X_21:=MA(X_20,6);
X_22:=(X_20-X_21)*1.2;
X_23:=(OPEN*2+REF(HIGH+LOW,1))/4*10;
X_24:=EMA(X_23,6)-EMA(X_23,55);
X_25:=EMA(X_24,6);
X_26:=(X_24-X_25)*0.06;
X_27:=X_22+X_26;
X_28:=X_27>0;
X_29:=MA(OPEN,3);
X_30:=MA(OPEN,13);
X_31:=MA(OPEN,34);
X_32:=X_29>X_30 AND X_31>=REF(X_31,1);
X_33:=EMA(OPEN,12);
X_34:=EMA(OPEN,50);
X_35:=X_33-X_34>0;
X_36:=OPEN/REF(CLOSE,1)-1;
X_37:=IF(X_32,2,0)+IF(X_28,2,0)+IF(X_35,2,0)+X_36;
X_38:=(X_7*X_37/10+X_16/1000)*100+25;
X_39:=X_13+X_15+X_38;
X_40:=NOT(NAMELIKE(313) OR NAMELIKE(314) OR NAMELIKE(315) OR VOL=0);
X_41:=SLOPE(X_39,2);
X_42:=SLOPE(X_6,2);
X_43:=SUM(X_10,2)*X_7;
土:IF(X_43>8 AND X_40,X_41*0.02-X_42*X_7,DRAWNULL)*X_7,COLORCYAN;

52
土-原始策略.txt Normal file
View File

@@ -0,0 +1,52 @@
X_1:=LLV(LOW,10);
X_2:=HHV(HIGH,25);
X_4:=3.2;
X_5:=3.5;
X_6:=REF(EMA((CLOSE-X_1)/(X_2-X_1)*4,4),1);
X_7:=IF(EXIST(CROSS(X_6,X_5),5),0,1);
X_10:=(REF(CLOSE,1)-REF(CLOSE,2))/REF(CLOSE,2)*100;
X_11:=2*(REF(CLOSE+HIGH+LOW,1)+OPEN)*100;
X_12:=(X_11/EMA(X_11,4)-1)*100;
X_13:=X_7*X_12;
X_14:=DMA(EMA(OPEN,12),SUM(REF(VOL,1),5)/3/CAPITAL);
X_15:=X_7*(OPEN-X_14)/X_14*200;
X_16:=0-(HHV(EMA(OPEN,5),14)-OPEN)/OPEN*5000;
X_17:=REF(CLOSE+LOW+3*OPEN+HIGH,1)/6;
X_18:=20*X_17+19*REF(X_17,1)+18*REF(X_17,2)+17*REF(X_17,3)+16*REF(X_17,4)+15*REF(X_17,5)+14*REF(X_17,6)+13*REF(X_17,7)+12*REF(X_17,8)+11*REF(X_17,9);
X_19:=10*REF(X_17,10)+9*REF(X_17,11)+8*REF(X_17,12)+7*REF(X_17,13)+6*REF(X_17,14)+5*REF(X_17,15)+4*REF(X_17,16)+3*REF(X_17,17)+2*REF(X_17,18)+REF(X_17,20);
X_20:=(X_18+X_19)/210;
X_21:=MA(X_20,6);
X_22:=(X_20-X_21)*1.2;
X_23:=(OPEN*2+REF(HIGH+LOW,1))/4*10;
X_24:=EMA(X_23,6)-EMA(X_23,55);
X_25:=EMA(X_24,6);
X_26:=(X_24-X_25)*0.06;
X_27:=X_22+X_26;
X_28:=X_27>0;
X_29:=MA(OPEN,3);
X_30:=MA(OPEN,13);
X_31:=MA(OPEN,34);
X_32:=X_29>X_30 AND X_31>=REF(X_31,1);
X_33:=EMA(OPEN,12);
X_34:=EMA(OPEN,50);
X_35:=X_33-X_34>0;
X_36:=OPEN/REF(CLOSE,1)-1;
X_37:=IF(X_32,2,0)+IF(X_28,2,0)+IF(X_35,2,0)+X_36;
X_38:=(X_7*X_37/10+X_16/1000)*100+25;
X_39:=X_13+X_15+X_38;
量比:= VOL / REF(MA(VOL,5),1);
X_44:= REF(VOL,1)>0 AND 量比>1;
X_40:= NOT(NAMELIKE(313) OR NAMELIKE(314) OR NAMELIKE(315)) AND REF(VOL,1)>0 AND VOL>0;
X_41:=SLOPE(X_39,2);
X_42:=SLOPE(X_6,2);
X_43:=SUM(X_10,2)*X_7;
ZB100:=REF(CLOSE,1)>REF(CLOSE,2)*1.09 AND REF(HIGH,1)=REF(CLOSE,1);
今开%:(OPEN-REF(CLOSE,1))/REF(CLOSE,1)*100;
土:IF(X_43>8 AND X_40,X_41*0.02-X_42*X_7,DRAWNULL)*X_7,COLORCYAN;
昨板数:BARSLASTCOUNT(ZB100),COLORRED;
细分行业:DRAWTEXT(1,0,HYBLOCK),COLOR00C0C0;

158
土策略回测_基础01.py Normal file
View File

@@ -0,0 +1,158 @@
import os
import pandas as pd
import numpy as np
from datetime import datetime, timedelta
# 回测配置
DATA_PATH = r'D:\gp_data\day' # 日线数据存放路径
START_DATE = '20150101' # 回测开始日期
END_DATE = '20231231' # 回测结束日期
STOP_LOSS = 0.03 # 止损比例3%
COMMISSION = 0.0003 # 单边交易佣金(万分之三)
# 策略信号计算函数
def calculate_signal(df):
"""计算土信号"""
df = df.copy()
# 计算基础参数
df['X_1'] = df['low'].rolling(10, min_periods=1).min() # 10日最低价
df['X_2'] = df['high'].rolling(25, min_periods=1).max() # 25日最高价
df['X_5'] = 3.5 # 固定阈值
# 标准化波动率X_6
df['X_6'] = ((df['close'] - df['X_1']) / (df['X_2'] - df['X_1']) * 4)
df['X_6'] = df['X_6'].ewm(span=4, adjust=False).mean().shift(1) # 4日EMA并滞后1日
# 波动率过滤X_7
df['cross_flag'] = (df['X_6'] > df['X_5']).astype(int) # 当前是否突破
df['cross_flag_shift'] = df['cross_flag'].shift(1) # 前一日状态
df['cross_event'] = (df['cross_flag'] > df['cross_flag_shift']).astype(int) # 上穿事件
df['X_7'] = df['cross_event'].rolling(5).max().replace(1, 0).shift(1) # 过去5日有上穿则为0
# 动量计算X_43
df['X_10'] = (df['close'].shift(1) - df['close'].shift(2)) / df['close'].shift(2) * 100
df['X_43'] = df['X_10'].rolling(2).sum() * df['X_7']
# 复合指标简化版X_39
ema_open_12 = df['open'].ewm(span=12, adjust=False).mean()
df['X_15'] = df['X_7'] * (df['open'] - ema_open_12) / ema_open_12 * 200
# 斜率计算
def calc_slope(series):
if len(series) < 2: return np.nan
return np.polyfit([0, 1], series.values, 1)[0]
df['X_41'] = df['X_15'].rolling(2).apply(calc_slope, raw=False)
df['X_42'] = df['X_6'].rolling(2).apply(calc_slope, raw=False)
# 生成最终信号
df['signal'] = np.where(
(df['X_43'] > 8) &
(~df['ts_code'].str.startswith(('313', '314', '315'))) & # 排除ST
(df['vol'] > 0) & # 排除零成交量
(df['X_7'] == 1), # 波动率过滤
(df['X_41'] * 0.02 - df['X_42'] * df['X_7']) * df['X_7'],
np.nan
)
return df
# 回测引擎核心逻辑
def backtest_strategy():
all_trades = []
# 遍历所有股票文件
for filename in os.listdir(DATA_PATH):
if not filename.endswith('_daily_data.txt'):
continue
# 读取数据
code = filename.split('_')[0]
df = pd.read_csv(
os.path.join(DATA_PATH, filename),
sep='\t',
parse_dates=['trade_date'],
dtype={'ts_code': str}
)
# 过滤日期范围
df = df[(df['trade_date'] >= pd.to_datetime(START_DATE)) &
(df['trade_date'] <= pd.to_datetime(END_DATE))]
if df.empty:
continue
# 按日期排序并计算信号
df = df.sort_values('trade_date')
df = calculate_signal(df)
# 提取有效信号
signals = df[df['signal'].notna()]
# 逐笔交易处理
for idx, row in signals.iterrows():
buy_date = row['trade_date']
buy_price = row['open']
# 获取次日数据
next_day = df[df['trade_date'] > buy_date].head(1)
if next_day.empty:
continue # 无次日数据(如停牌)
sell_date = next_day['trade_date'].values[0]
sell_high = next_day['high'].values[0]
sell_low = next_day['low'].values[0]
# 计算止损价
stop_price = buy_price * (1 - STOP_LOSS)
# 确定卖出价格
if sell_low < stop_price:
sell_price = stop_price # 触发止损
else:
sell_price = sell_high # 按次日最高价卖出
# 计算收益率(扣除双边佣金)
ret = (sell_price / buy_price - 1) - 2 * COMMISSION
success = 1 if ret > 0 else 0
# 记录交易
all_trades.append({
'code': code,
'buy_date': buy_date.strftime('%Y%m%d'),
'sell_date': sell_date.strftime('%Y%m%d'),
'buy_price': buy_price,
'sell_price': sell_price,
'return': ret,
'success': success,
'hold_days': 1
})
# 绩效分析
if not all_trades:
print("未产生任何交易信号")
return
results = pd.DataFrame(all_trades)
# 关键指标计算
total_trades = len(results)
win_rate = results['success'].mean()
avg_return = results['return'].mean()
annualized_return = avg_return * 240 # 假设年240个交易日
# 输出结果
print(f"回测期间:{START_DATE}{END_DATE}")
print(f"总交易次数:{total_trades}")
print(f"胜率:{win_rate:.2%}")
print(f"单次平均收益率:{avg_return:.2%}")
print(f"年化收益率(估算):{annualized_return:.2%}")
# 保存详细结果
results.to_csv('trading_records.csv', index=False)
print("详细交易记录已保存至 trading_records.csv")
if __name__ == '__main__':
backtest_strategy()