日频回测demos
由qxiao创建,最终由qxiao 被浏览 1 用户
期货日频策略一:唐奇安通道突破(沪深300股指期货 IF)
使用 20 日唐奇安通道,收盘价突破 20 日最高价时做多,跌破 20 日最低价时做空,持仓期间自动移仓换月。
from datetime import datetime
from bigquant import bigtrader, dai
from bigtrader.constant import Direction, OrderType
def initialize(context: bigtrader.IContext):
context.logger.info("初始化:计算唐奇安通道信号...")
channel_period = 20
sql = f"""
SELECT
date,
instrument,
dominant,
close,
high,
low,
m_lag(m_max(high, {channel_period}), 1) AS upper_band,
m_lag(m_min(low, {channel_period}), 1) AS lower_band,
CASE
WHEN close >= upper_band THEN 1
WHEN close <= lower_band THEN -1
ELSE 0
END AS signal
FROM cn_future_bar1d
LEFT JOIN cn_future_dominant USING (date, instrument)
WHERE instrument = 'IF8888.CFE'
"""
df = dai.query(sql, filters={
"date": [
context.add_trading_days(context.start_date, -(channel_period + 10)),
context.end_date
]
}).df()
context.data = df
context.lots = 1
context.logger.info(f"数据加载完成,共 {len(df)} 条记录")
def handle_data(context: bigtrader.IContext, data: bigtrader.IBarData):
today = data.current_dt.strftime('%Y-%m-%d')
df_today = context.data[context.data['date'] == today]
if df_today.empty:
return
signal = int(df_today['signal'].values[0])
price = float(df_today['close'].values[0])
dominant_symbol = df_today['dominant'].values[0]
positions = context.get_account_positions()
position_symbol = list(positions.keys())[0] if positions else dominant_symbol
long_qty = context.get_account_position(position_symbol, direction=Direction.LONG).avail_qty
short_qty = context.get_account_position(position_symbol, direction=Direction.SHORT).avail_qty
total_qty = long_qty + short_qty
# ── 移仓换月 ──────────────────────────────────────────────
if dominant_symbol != position_symbol:
context.logger.info(f"移仓换月:{position_symbol} -> {dominant_symbol}")
if long_qty > 0:
context.sell_close(position_symbol, long_qty, price, order_type=OrderType.MARKET)
context.buy_open(dominant_symbol, long_qty, price, order_type=OrderType.MARKET)
elif short_qty > 0:
context.buy_close(position_symbol, short_qty, price, order_type=OrderType.MARKET)
context.sell_open(dominant_symbol, short_qty, price, order_type=OrderType.MARKET)
position_symbol = dominant_symbol
# ── 信号交易 ──────────────────────────────────────────────
if long_qty > 0:
if signal == -1:
context.logger.info("平多开空:价格跌破 20 日最低价")
context.sell_close(position_symbol, long_qty, price, order_type=OrderType.MARKET)
context.sell_open(dominant_symbol, context.lots, price, order_type=OrderType.MARKET)
elif short_qty > 0:
if signal == 1:
context.logger.info("平空开多:价格突破 20 日最高价")
context.buy_close(position_symbol, short_qty, price, order_type=OrderType.MARKET)
context.buy_open(dominant_symbol, context.lots, price, order_type=OrderType.MARKET)
elif total_qty == 0:
if signal == 1:
context.logger.info("空仓开多:价格突破 20 日最高价")
context.buy_open(dominant_symbol, context.lots, price, order_type=OrderType.MARKET)
elif signal == -1:
context.logger.info("空仓开空:价格跌破 20 日最低价")
context.sell_open(dominant_symbol, context.lots, price, order_type=OrderType.MARKET)
performance = bigtrader.run(
market=bigtrader.Market.CN_FUTURE,
frequency=bigtrader.Frequency.DAILY,
start_date="2020-01-01",
end_date=datetime.now().strftime("%Y-%m-%d"),
capital_base=5_000_000,
initialize=initialize,
handle_data=handle_data,
order_price_field_buy='open',
order_price_field_sell='open',
)
期货日频策略二:双均线交叉趋势跟踪(螺纹钢 rb)
短期均线(SMA5)上穿长期均线(SMA20)时金叉做多,下穿时死叉做空,持仓期间自动移仓换月。
from datetime import datetime
from bigquant import bigtrader, dai
from bigtrader.constant import Direction, OrderType
def initialize(context: bigtrader.IContext):
context.logger.info("初始化:计算双均线交叉信号...")
short_period = 5
long_period = 20
sql = f"""
SELECT
date,
instrument,
dominant,
close,
m_avg(close, {short_period}) AS sma_short,
m_avg(close, {long_period}) AS sma_long,
m_lag(m_avg(close, {short_period}), 1) AS sma_short_prev,
m_lag(m_avg(close, {long_period}), 1) AS sma_long_prev,
CASE
WHEN sma_short > sma_long AND sma_short_prev <= sma_long_prev THEN 1
WHEN sma_short < sma_long AND sma_short_prev >= sma_long_prev THEN -1
ELSE 0
END AS signal
FROM cn_future_bar1d
LEFT JOIN cn_future_dominant USING (date, instrument)
WHERE instrument = 'rb8888.SHF'
"""
df = dai.query(sql, filters={
"date": [
context.add_trading_days(context.start_date, -(long_period + 10)),
context.end_date
]
}).df()
context.data = df
context.lots = 1
context.logger.info(f"数据加载完成,共 {len(df)} 条记录")
def handle_data(context: bigtrader.IContext, data: bigtrader.IBarData):
today = data.current_dt.strftime('%Y-%m-%d')
df_today = context.data[context.data['date'] == today]
if df_today.empty:
return
signal = int(df_today['signal'].values[0])
price = float(df_today['close'].values[0])
dominant_symbol = df_today['dominant'].values[0]
positions = context.get_account_positions()
position_symbol = list(positions.keys())[0] if positions else dominant_symbol
long_qty = context.get_account_position(position_symbol, direction=Direction.LONG).avail_qty
short_qty = context.get_account_position(position_symbol, direction=Direction.SHORT).avail_qty
total_qty = long_qty + short_qty
# ── 移仓换月 ──────────────────────────────────────────────
if dominant_symbol != position_symbol:
context.logger.info(f"移仓换月:{position_symbol} -> {dominant_symbol}")
if long_qty > 0:
context.sell_close(position_symbol, long_qty, price, order_type=OrderType.MARKET)
context.buy_open(dominant_symbol, long_qty, price, order_type=OrderType.MARKET)
elif short_qty > 0:
context.buy_close(position_symbol, short_qty, price, order_type=OrderType.MARKET)
context.sell_open(dominant_symbol, short_qty, price, order_type=OrderType.MARKET)
position_symbol = dominant_symbol
# ── 信号交易 ──────────────────────────────────────────────
if long_qty > 0:
if signal == -1:
context.logger.info("死叉:平多开空")
context.sell_close(position_symbol, long_qty, price, order_type=OrderType.MARKET)
context.sell_open(dominant_symbol, context.lots, price, order_type=OrderType.MARKET)
elif short_qty > 0:
if signal == 1:
context.logger.info("金叉:平空开多")
context.buy_close(position_symbol, short_qty, price, order_type=OrderType.MARKET)
context.buy_open(dominant_symbol, context.lots, price, order_type=OrderType.MARKET)
elif total_qty == 0:
if signal == 1:
context.logger.info("空仓金叉:开多")
context.buy_open(dominant_symbol, context.lots, price, order_type=OrderType.MARKET)
elif signal == -1:
context.logger.info("空仓死叉:开空")
context.sell_open(dominant_symbol, context.lots, price, order_type=OrderType.MARKET)
performance = bigtrader.run(
market=bigtrader.Market.CN_FUTURE,
frequency=bigtrader.Frequency.DAILY,
start_date="2020-01-01",
end_date=datetime.now().strftime("%Y-%m-%d"),
capital_base=1_000_000,
initialize=initialize,
handle_data=handle_data,
order_price_field_buy='open',
order_price_field_sell='open',
)
期权日频策略一:保护性看跌策略(Protective Put)
持有沪深300ETF(510300.SH)现货多头仓位,同时买入下月到期的平值或虚值1档看跌期权作为下行保护。每5个交易日检查并调整期权保护仓位,期权仓位与ETF现货仓位保持1:1对冲比例。
import math
from datetime import datetime
from bigquant import bigtrader, dai
from dateutil.relativedelta import relativedelta
def get_next_month(cur_date):
date = datetime.strptime(cur_date, '%Y-%m-%d')
next_month_date = date + relativedelta(months=1)
return f"{next_month_date.year % 100:02d}{next_month_date.month:02d}"
def get_option_contracts(my_month, option_type, underlying, current_date):
sql = f"""
SELECT strike_price, english_name, instrument
FROM cn_option_basic_info
WHERE english_name LIKE '{underlying}{option_type}{my_month}%'
AND list_date <= '{current_date}'
ORDER BY strike_price
"""
try:
return dai.query(sql).df()
except Exception:
return None
def find_nearest_option(target_price, my_month, option_type, underlying, current_date):
contracts_df = get_option_contracts(my_month, option_type, underlying, current_date)
if contracts_df is None or contracts_df.empty:
return None, None, None
contracts_df['price_diff'] = abs(contracts_df['strike_price'] - target_price)
nearest = contracts_df.loc[contracts_df['price_diff'].idxmin()]
return nearest['strike_price'], nearest['english_name'], nearest['instrument']
def initialize(context: bigtrader.IContext):
context.set_commission(bigtrader.PerOrder(buy_cost=0.0003, sell_cost=0.0013, min_cost=5))
context.etf = "510300.SH"
context.option_prefix = "510300"
context.rebalance_days = 5
context.otm_level = 1
context.strike_step = 0.05
context.hedge_ratio = 1.0
context.etf_position_ratio = 0.80
sql = """
SELECT date, instrument, close
FROM cn_fund_bar1d
WHERE instrument = '510300.SH'
ORDER BY date
"""
df = dai.query(sql, filters={"date": [context.start_date, context.end_date]}).df()
context.etf_data = df
def handle_data(context: bigtrader.IContext, data: bigtrader.IBarData):
current_date = context.current_dt.strftime('%Y-%m-%d')
etf_price = data.current(context.etf, "close")
if etf_price is None or math.isnan(etf_price) or etf_price <= 0:
return
# 维持 ETF 现货多头仓位
etf_position = context.portfolio.positions.get(context.etf)
etf_long_qty = etf_position.long_avail_qty() if etf_position else 0
target_etf_qty = int(context.portfolio.total_value * context.etf_position_ratio / etf_price / 100) * 100
if etf_long_qty == 0 and target_etf_qty > 0:
context.order_target(context.etf, target_etf_qty)
# 确定下月看跌期权合约
my_month = get_next_month(current_date)
put_target_price = etf_price - context.strike_step * context.otm_level
put_strike, put_contract, put_instrument = find_nearest_option(
put_target_price, my_month, 'P', context.option_prefix, current_date
)
if put_strike is None:
return
context.subscribe_bar(put_instrument)
if context.trading_day_index % context.rebalance_days != 0:
return
put_price = data.current(put_instrument, "close")
if put_price is None or math.isnan(put_price) or put_price <= 0:
return
current_etf_qty = etf_long_qty if etf_long_qty > 0 else target_etf_qty
target_put_qty = max(1, int(current_etf_qty * context.hedge_ratio / 10000))
# 调整期权仓位(省略持仓扫描逻辑,完整代码见原始文档)
context.buy_open(put_instrument, target_put_qty)
performance = bigtrader.run(
market=bigtrader.Market.CN_STOCK_OPTION,
frequency=bigtrader.Frequency.DAILY,
start_date="2026-01-01",
end_date=datetime.now().strftime("%Y-%m-%d"),
capital_base=1000000,
instruments=["510300.SH"],
benchmark="510300.SH",
initialize=initialize,
handle_data=handle_data,
)
期权日频策略二:牛市看涨期权价差策略(Bull Call Spread)
买入平值(ATM)看涨期权 + 卖出虚值(OTM)看涨期权,两腿合约数量相同,净权利金支出有限。每 5 个交易日检查并调仓,选择下月到期合约。
- 最大收益 = (卖出行权价 - 买入行权价) × 合约单位 × 张数 - 净权利金
- 最大亏损 = 净权利金支出(有限风险)
- 适用场景:温和看涨,希望以较低成本参与上涨行情
import math
from datetime import datetime
from bigquant import bigtrader, dai
from dateutil.relativedelta import relativedelta
def initialize(context: bigtrader.IContext):
context.set_commission(bigtrader.PerOrder(buy_cost=0.0003, sell_cost=0.0013, min_cost=5))
context.sh50_etf = "510050.SH"
context.rebalance_days = 5
context.capital_ratio = 0.05
context.buy_otm_level = 0
context.sell_otm_level = 2
context.strike_step = 0.05
# 预缓存 ETF 日线数据
etf_df = dai.query(
"SELECT date, instrument, close FROM cn_fund_bar1d WHERE instrument = '510050.SH' ORDER BY date",
filters={"date": [context.start_date, context.end_date]}
).df()
context.etf_data = etf_df
# 预缓存所有 510050 看涨期权基本信息
option_df = dai.query(
"SELECT instrument, english_name, strike_price, list_date FROM cn_option_basic_info WHERE english_name LIKE '510050C%' ORDER BY english_name"
).df()
context.option_info_by_instrument = {}
context.option_contracts_by_month = {}
for _, row in option_df.iterrows():
context.option_info_by_instrument[row['instrument']] = {
'english_name': row['english_name'],
'strike_price': row['strike_price'],
}
month_key = row['english_name'][7:11]
if month_key not in context.option_contracts_by_month:
context.option_contracts_by_month[month_key] = []
context.option_contracts_by_month[month_key].append({
'instrument': row['instrument'],
'english_name': row['english_name'],
'strike_price': row['strike_price'],
'list_date': str(row['list_date']),
})
def get_next_month(cur_date):
date = datetime.strptime(cur_date, '%Y-%m-%d')
next_month_date = date + relativedelta(months=1)
return f"{next_month_date.year % 100:02d}{next_month_date.month:02d}"
def find_nearest_option(context, target_price, my_month, current_date):
contracts = context.option_contracts_by_month.get(my_month, [])
valid = [c for c in contracts if c['list_date'] <= current_date]
if not valid:
return None, None, None
nearest = min(valid, key=lambda c: abs(c['strike_price'] - target_price))
return nearest['strike_price'], nearest['english_name'], nearest['instrument']
def handle_data(context: bigtrader.IContext, data: bigtrader.IBarData):
current_date = context.current_dt.strftime('%Y-%m-%d')
current_etf_price = data.current(context.sh50_etf, "close")
if current_etf_price is None or math.isnan(current_etf_price):
return
my_month = get_next_month(current_date)
buy_strike, buy_contract, buy_instrument = find_nearest_option(
context, current_etf_price + context.strike_step * context.buy_otm_level, my_month, current_date)
sell_strike, sell_contract, sell_instrument = find_nearest_option(
context, current_etf_price + context.strike_step * context.sell_otm_level, my_month, current_date)
if buy_strike is None or sell_strike is None or buy_strike >= sell_strike:
return
if context.trading_day_index % context.rebalance_days != 0:
return
buy_price = data.current(buy_instrument, "close")
sell_price = data.current(sell_instrument, "close")
if buy_price is None or sell_price is None or math.isnan(buy_price) or math.isnan(sell_price):
return
if buy_price <= 0 or sell_price <= 0:
return
net_cost_per_contract = (buy_price - sell_price) * 10000
if net_cost_per_contract <= 0:
return
quantity = int(context.portfolio.total_value * context.capital_ratio / net_cost_per_contract)
if quantity <= 0:
return
# 买入腿 + 卖出腿(省略持仓扫描换仓逻辑,完整代码见原始文档)
context.buy_open(buy_instrument, quantity)
context.sell_open(sell_instrument, quantity)
performance = bigtrader.run(
market=bigtrader.Market.CN_STOCK_OPTION,
frequency=bigtrader.Frequency.DAILY,
start_date="2025-01-01",
end_date=datetime.now().strftime("%Y-%m-%d"),
capital_base=500000,
instruments=["510050.SH"],
benchmark='510050.SH',
initialize=initialize,
handle_data=handle_data,
)
\