CTA布林带+MACD

In [5]:
import dai
import numpy as np
import warnings
import vectorbt as vbt
import optuna
import pandas_ta as ta

warnings.filterwarnings('ignore')
In [36]:
def objective(trial):
    sql = """
    SELECT * FROM cn_future_bar1m
    WHERE instrument = 'pb2405.SHF'
    ORDER BY date
    """
    df = dai.query(sql, filters={'date': ['2023-05-12', '2023-11-10']}).df()

    # 这里编写你要调节的参数
    macd_patience = trial.suggest_int('macd_patience', 2, 10)
    boll_dev = trial.suggest_float('boll_dev', 1.5, 3.0, step=0.1)
    stoploss = trial.suggest_int('stoploss', 3, 10)
    stopwin = trial.suggest_int('stopwin', int(stoploss * 1.5), int(stoploss * 3.0))

    boll_window = trial.suggest_int('boll_window', 20, 50)
    fast_window = trial.suggest_int('fast_window', 6, 15)
    slow_window = trial.suggest_int('slow_window', 21, 40)
    macd_window = trial.suggest_int('macd_window', 2, 5)

    # 编写MACD
    df[['macd', 'macd_signal', 'macd_hist']] = ta.macd(df['close'], fast=fast_window, slow=slow_window, signal=macd_window)
    dk = ta.bbands(df['close'], length=boll_window, std=boll_dev)
    df['bb_upper'], df['bb_middle'], df['bb_lower'] = dk[f'BBU_{boll_window}_{boll_dev}'], dk[f'BBM_{boll_window}_{boll_dev}'], dk[f'BBL_{boll_window}_{boll_dev}']

    # 计算MACD金叉和死叉信号
    df['macd_golden_cross'] = (df['macd'] > df['macd_signal']) & (df['macd'].shift() < df['macd_signal'].shift())
    df['macd_death_cross'] = (df['macd'] < df['macd_signal']) & (df['macd'].shift() > df['macd_signal'].shift())

    # 计算金叉和死叉信号的计数
    df['macd_golden_cross_count'] = df['macd_golden_cross'].rolling(macd_patience).sum()
    df['macd_death_cross_count'] = df['macd_death_cross'].rolling(macd_patience).sum()

    # 根据信号计算买入和卖出信号
    df['macd_buy_signal'] = np.where((df['macd_golden_cross_count'] > 0) & (df['macd_death_cross_count'] == 0), 1, 0)
    df['macd_sell_signal'] = np.where((df['macd_death_cross_count'] > 0) & (df['macd_golden_cross_count'] == 0), 1, 0)

    # 计算买入和卖出点
    entries_long = (df['macd_buy_signal'] == 1) & (df['close'] > df['bb_upper'])  
    entries_short = (df['macd_sell_signal'] == 1) & (df['close'] < df['bb_lower'])  

    exits_long = (df['macd'] < df['macd_signal']) & (df['close'] < df['bb_lower']) 
    exits_short = (df['macd'] > df['macd_signal']) & (df['close'] > df['bb_upper']) 

    entries_long = entries_long.astype(bool)
    entries_short = entries_short.astype(bool)
    exits_long = exits_long.astype(bool)
    exits_short = exits_short.astype(bool)

    pf = vbt.Portfolio.from_signals(
        close=df['close'],
        entries=entries_long,
        exits=exits_long,
        short_entries=entries_short,
        short_exits=exits_short,
        fees=0.03 / 100,
        sl_stop=stoploss / 100,
        sl_trail=True,
        tp_stop=stopwin / 100,
        size=1,
        freq='1min',
        direction='both'
    )

    return -pf.sharpe_ratio()
In [37]:
study = optuna.create_study()  # 使用optuna创建Study
study.optimize(objective, n_trials=100)  # 设置Study的优化次数
In [38]:
study.best_params
Out[38]:
{'macd_patience': 9,
 'boll_dev': 2.8,
 'stoploss': 10,
 'stopwin': 16,
 'boll_window': 50,
 'fast_window': 10,
 'slow_window': 24,
 'macd_window': 2}
In [21]:
sql = """
SELECT * FROM cn_future_bar1m
WHERE instrument = 'pb2405.SHF'
ORDER BY date
"""
df = dai.query(sql, filters={'date': ['2023-05-12', '2023-11-10']}).df()
In [39]:
fast_window = 10
slow_window = 24
macd_window = 2
boll_window = 50
boll_dev = 2.8
macd_patience = 9
stoploss = 10
stopwin = 16
In [41]:
df[['macd', 'macd_signal', 'macd_hist']] = ta.macd(df['close'], fast=fast_window, slow=slow_window, signal=macd_window)
dk = ta.bbands(df['close'], length=boll_window, std=boll_dev)
df['bb_upper'], df['bb_middle'], df['bb_lower'] = dk[f'BBU_{boll_window}_{boll_dev}'], dk[f'BBM_{boll_window}_{boll_dev}'], dk[f'BBL_{boll_window}_{boll_dev}']

# 计算MACD金叉和死叉信号
df['macd_golden_cross'] = (df['macd'] > df['macd_signal']) & (df['macd'].shift() < df['macd_signal'].shift())
df['macd_death_cross'] = (df['macd'] < df['macd_signal']) & (df['macd'].shift() > df['macd_signal'].shift())

# 计算金叉和死叉信号的计数
df['macd_golden_cross_count'] = df['macd_golden_cross'].rolling(macd_patience).sum()
df['macd_death_cross_count'] = df['macd_death_cross'].rolling(macd_patience).sum()

# 根据信号计算买入和卖出信号
df['macd_buy_signal'] = np.where((df['macd_golden_cross_count'] > 0) & (df['macd_death_cross_count'] == 0), 1, 0)
df['macd_sell_signal'] = np.where((df['macd_death_cross_count'] > 0) & (df['macd_golden_cross_count'] == 0), 1, 0)

# 计算买入和卖出点
entries_long = (df['macd_buy_signal'] == 1) & (df['close'] > df['bb_upper'])  
entries_short = (df['macd_sell_signal'] == 1) & (df['close'] < df['bb_lower'])  

exits_long = (df['macd'] < df['macd_signal']) & (df['close'] < df['bb_lower']) 
exits_short = (df['macd'] > df['macd_signal']) & (df['close'] > df['bb_upper']) 

entries_long = entries_long.astype(bool)
entries_short = entries_short.astype(bool)
exits_long = exits_long.astype(bool)
exits_short = exits_short.astype(bool)
In [42]:
pf = vbt.Portfolio.from_signals(
    close=df['close'],
    entries=entries_long,
    exits=exits_long,
    short_entries=entries_short,
    short_exits=exits_short,
    fees=0.03 / 100,
    sl_stop=stoploss / 100,
    sl_trail=True,
    tp_stop=stopwin / 100,
    size=1,
    freq='1min',
    direction='both'
)
In [43]:
pf.sharpe_ratio()
Out[43]:
0.7200470201261924
In [44]:
pf.total_return()
Out[44]:
-1.0855893650543353
In [ ]: