原创内容第971篇,专注AGI+,AI量化投资、个人成长与财富自由。指标的核心思想是通过对价格进行三次指数平滑来过滤掉短期市场波动(噪音),从而更清晰地识别中长期的趋势方向、动量的变化以及潜在的转折点(背离)。
from datetime import dateimport pandas as pdfrom tqsdk import TqApi, TqAuth, TqBacktest, TargetPosTask, BacktestFinishedfrom tqsdk.ta import ATR
import osdef get_env_var(name, default=None): value = os.environ.get(name) if value is None and default is None: raise EnvironmentError(f"环境变量 {name} 未设置") return value or defaultusername = get_env_var('TQ_USERNAME')password = get_env_var('TQ_PASSWORD')
SYMBOL = "CFFEX.IC2306" POSITION_SIZE = 30 START_DATE = date(2022, 11, 1) END_DATE = date(2023, 4, 30)
TRIX_PERIOD = 12 SIGNAL_PERIOD = 9 MA_PERIOD = 60
SIGNAL_THRESHOLD = 0.05
ATR_PERIOD = 14 STOP_LOSS_MULTIPLIER = 2.0 TAKE_PROFIT_MULTIPLIER = 3.0 MAX_HOLDING_DAYS = 15
current_direction = 0 entry_price = 0 stop_loss_price = 0 entry_date = None trade_count = 0 win_count = 0
def calculate_trix(close_prices, period): """计算TRIX指标和信号线""" ema1 = close_prices.ewm(span=period, adjust=False).mean()
ema2 = ema1.ewm(span=period, adjust=False).mean() ema3 = ema2.ewm(span=period, adjust=False).mean() trix = 100 * (ema3 / ema3.shift(1) - 1) signal = trix.rolling(SIGNAL_PERIOD).mean()
return trix, signal
print("开始运行TRIX指标期货策略...")print(f"品种: {SYMBOL}, 回测周期: {START_DATE} - {END_DATE}")print(f"TRIX参数: 周期={TRIX_PERIOD}, 信号线周期={SIGNAL_PERIOD}")
api = TqApi(web_gui=True,backtest=TqBacktest(start_dt=START_DATE, end_dt=END_DATE), auth=TqAuth(username, password))
klines = api.get_kline_serial(SYMBOL, 60 * 60 * 24)
target_pos = TargetPosTask(api, SYMBOL)
try: while True: api.wait_update()
if api.is_changing(klines.iloc[-1], "datetime"): if len(klines) max(TRIX_PERIOD, SIGNAL_PERIOD, MA_PERIOD, ATR_PERIOD) + 10: continue
klines['trix'], klines['signal'] = calculate_trix(klines.close, TRIX_PERIOD)
klines['ma'] = klines.close.rolling(window=MA_PERIOD).mean()
atr_data = ATR(klines, ATR_PERIOD)
current_price = float(klines.close.iloc[-1]) current_datetime = pd.to_datetime(klines.datetime.iloc[-1], unit='ns') current_trix = float(klines.trix.iloc[-1]) previous_trix = float(klines.trix.iloc[-2]) current_signal = float(klines.signal.iloc[-1]) previous_signal = float(klines.signal.iloc[-2]) current_ma = float(klines.ma.iloc[-1]) current_atr = float(atr_data.atr.iloc[-1])
trix_diff = current_trix - current_signal previous_trix_diff = previous_trix - previous_signal
print(f"日期: {current_datetime.strftime('%Y-%m-%d')}, 价格: {current_price:.2f}") print(f"TRIX: {current_trix:.4f}, 信号线: {current_signal:.4f}, 差值: {trix_diff:.4f}")
if current_direction == 0: if
previous_trix < previous_signal and current_trix > current_signal: current_direction = 1 target_pos.set_target_volume(POSITION_SIZE) entry_price = current_price stop_loss_price = entry_price - STOP_LOSS_MULTIPLIER * current_atr take_profit_price = entry_price + TAKE_PROFIT_MULTIPLIER * current_atr print(f"多头开仓: 价格={entry_price}, 止损={stop_loss_price:.2f}, 止盈={take_profit_price:.2f}")
elif previous_trix > previous_signal and current_trix < current_signal: current_direction = -1 target_pos.set_target_volume(-POSITION_SIZE) entry_price = current_price stop_loss_price = entry_price + STOP_LOSS_MULTIPLIER * current_atr take_profit_price = entry_price - TAKE_PROFIT_MULTIPLIER * current_atr print(f"空头开仓: 价格={entry_price}, 止损={stop_loss_price:.2f}, 止盈={take_profit_price:.2f}")
elif current_direction == 1: if current_price <= stop_loss_price: profit_pct = (current_price - entry_price) / entry_price * 100 target_pos.set_target_volume(0) current_direction = 0 print(f"多头止损平仓: 价格={current_price}, 盈亏={profit_pct:.2f}%")
elif current_price >= take_profit_price: profit_pct = (current_price - entry_price) / entry_price * 100 target_pos.set_target_volume(0) current_direction = 0 print(f"多头止盈平仓: 价格={current_price}, 盈亏={profit_pct:.2f}%")
elif previous_trix > previous_signal and current_trix < current_signal: profit_pct = (current_price - entry_price) / entry_price * 100 target_pos.set_target_volume(0) current_direction = 0 print(f"多头信号平仓: 价格={current_price}, 盈亏={profit_pct:.2f}%")
elif current_direction == -1: if current_price >= stop_loss_price: profit_pct = (entry_price - current_price) / entry_price * 100 target_pos.set_target_volume(0) current_direction = 0 print(f"空头止损平仓: 价格={current_price}, 盈亏={profit_pct:.2f}%")
elif current_price <= take_profit_price: profit_pct = (entry_price - current_price) / entry_price * 100
target_pos.set_target_volume(0) current_direction = 0 print(f"空头止盈平仓: 价格={current_price}, 盈亏={profit_pct:.2f}%")
elif previous_trix < previous_signal and current_trix > current_signal: profit_pct = (entry_price - current_price) / entry_price * 100 target_pos.set_target_volume(0) current_direction = 0 print(f"空头信号平仓: 价格={current_price}, 盈亏={profit_pct:.2f}%")
except BacktestFinished as e: print("回测结束") api.close()
规则的逻辑是因子和阈值,比如咱们昨天策略里的aroon指标>70,来刻画一种走势状态。规则当然是一目了然,缺点是比较机械,为何是70呢?aroon指标本身也是一个因子,我们可以轻松计算它与未来N期收益率之间的相关性(IC值)。在单因子回测的时候,我们通常会设计百分位的阈值,而不是具体某个值,比如因子值>过去一段时间80%分位点时,做多等,然后通过止赢/止损位来平仓。另外一个就是N期收益率的问题,在多因子合成时建模时,需要一个标签。关于标签的设计:通过是未来N天的收益率。这里的标签其实存在问题,比如,你的策略设定的是2%止损位,那在这个5天里,有没有可能,已经下行了2%?逻辑上当然可能,也可能突破了止盈位。另外就是如果预测的是未来5天,那策略的持有期就应该设定为至少5天。还有5天是一个离散值,有没有可能前面涨不少,然后第5天回落,或者前面跌不少,然后第5天回调。这都是有可能的。问题就在于,使用一个第几天的标签,这个离散的状态刻画不了连续的价格。代码和数据下载:AI量化实验室——2025量化投资的星辰大海AI量化实验室 星球,已经运行三年多,1800+会员。
aitrader代码,因子表达式引擎、遗传算法(Deap)因子挖掘引擎等,支持vnpy,qlib,backtrader和bt引擎,内置多个年化30%+的策略,每周五迭代一次,代码和数据在星球全部开源。
点击 “查看原文”,直接访问策略集合。