提交基础内容
This commit is contained in:
8
.idea/.gitignore
generated
vendored
Normal file
8
.idea/.gitignore
generated
vendored
Normal file
@@ -0,0 +1,8 @@
|
|||||||
|
# 默认忽略的文件
|
||||||
|
/shelf/
|
||||||
|
/workspace.xml
|
||||||
|
# 基于编辑器的 HTTP 客户端请求
|
||||||
|
/httpRequests/
|
||||||
|
# Datasource local storage ignored files
|
||||||
|
/dataSources/
|
||||||
|
/dataSources.local.xml
|
||||||
19
.idea/inspectionProfiles/Project_Default.xml
generated
Normal file
19
.idea/inspectionProfiles/Project_Default.xml
generated
Normal 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>
|
||||||
6
.idea/inspectionProfiles/profiles_settings.xml
generated
Normal file
6
.idea/inspectionProfiles/profiles_settings.xml
generated
Normal 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
7
.idea/misc.xml
generated
Normal 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
8
.idea/modules.xml
generated
Normal 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
10
.idea/土信号.iml
generated
Normal 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
41
土-优化01策略.txt
Normal 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
52
土-原始策略.txt
Normal 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
158
土策略回测_基础01.py
Normal 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()
|
||||||
Reference in New Issue
Block a user