Tổng hợp chiến lược tạo tín hiệu giao dịch VN30F1M

Tổng hợp các chiến lược tạo tín hiệu giao dịch từ dữ liệu phái sinh VN30F1M (Python)

Chào mọi người, mình muốn chia sẻ một số chiến lược đơn giản để tạo tín hiệu mua/bán (position) trên dữ liệu phái sinh VN30F1M sử dụng Python. Dữ liệu được lấy từ thư viện xnoapi, và có thể backtest trực tiếp bằng hàm Backtest_Derivates. Ngoài ra, bạn cũng có thể sử dụng dữ liệu cơ sở từ thư viện xnoapi, hướng dẫn ở đây https://github.com/xnoproject/xnoapi

:package: Lấy dữ liệu từ xnoapi

from xnoapi import client
from xnoapi.vn.data import derivatives

client(apikey="...")

# Lấy dữ liệu 1 phút của VN30F1M
df = derivatives.get_hist("VN30F1M", "1m")

1. Daily Strategy (Chiến lược theo SMA20 ngày)

  • Dựa trên giá đóng cửa hằng ngày so với SMA20.
  • Nếu Close > SMA20 → Mua
  • Nếu Close < SMA20 → Bán
import pandas as pd

def gen_position(df: pd.DataFrame) -> pd.DataFrame:
    df = df.copy()
    df['datetime'] = pd.to_datetime(df['Date'] + ' ' + df['time'])
    df.set_index('datetime', inplace=True)

    daily = df.resample('1D').agg({
        'Open': 'first',
        'High': 'max',
        'Low': 'min',
        'Close': 'last',
        'volume': 'sum'
    }).dropna()

    daily['SMA20'] = daily['Close'].rolling(window=20).mean()
    daily['daily_position'] = 0
    daily.loc[daily['Close'] > daily['SMA20'], 'daily_position'] = 1
    daily.loc[daily['Close'] < daily['SMA20'], 'daily_position'] = -1

    daily_signals = daily[['daily_position']]
    df = df.merge(daily_signals, left_on=df.index.date, right_on=daily_signals.index.date, how='left')
    df = df.rename(columns={'daily_position': 'position'}).drop(columns=['key_0'])

    return df

df_pos = gen_position(df)
backtest = Backtest_Derivates(df_pos, pnl_type="raw")
backtest.PNL().plot()

2. Normal Strategy (So sánh Close với Median Rolling 20)

import numpy as np

def gen_position(df):
    return df.assign(
        position=np.sign(df["Close"] - df["Close"].rolling(20).median())
    )

3. Simple ML Strategy (Ridge Regression với giá Close)

from sklearn.linear_model import Ridge

def gen_position(df):
    model = Ridge()
    X = df["Close"].shift(1).fillna(method='bfill').values.reshape(-1, 1)
    y = df["Close"].values
    model.fit(X, y)

    pred = model.predict(X)
    df["predicted_close"] = pred

    df["position"] = 0
    df.loc[pred > df["Close"], "position"] = 1
    df.loc[pred < df["Close"], "position"] = -1

    return df

df_pos = gen_position(df)
backtest = Backtest_Derivates(df_pos, pnl_type="raw")
backtest.PNL().plot()

4. TA ML Strategy (Kết hợp Moving Average + ML)

from sklearn.linear_model import Ridge

def gen_position(df):
    df["ma"] = df["Close"].rolling(window=5).mean().fillna(method='bfill')
    model = Ridge()
    X = df[["ma"]].shift(1).fillna(method='bfill')
    y = df["Close"]
    model.fit(X, y)

    pred = model.predict(X)
    df["predicted_close"] = pred

    df["position"] = 0
    df.loc[pred > df["Close"], "position"] = -1
    df.loc[pred < df["Close"], "position"] = 1

    return df

df_pos = gen_position(df)
backtest = Backtest_Derivates(df_pos, pnl_type="raw")
backtest.PNL().plot()

Kết luận

  • Các chiến lược trên đều đơn giản và có thể tùy chỉnh thêm các yếu tố như khối lượng, biến động, hoặc tín hiệu đa khung thời gian.
  • Chiến lược ML là điểm khởi đầu tốt để phát triển mô hình học sâu hơn (LSTM, Random Forest, XGBoost…).
  • Backtest_Derivates là công cụ kiểm tra chiến lược tiện lợi với biểu đồ PnL trực quan.

Nếu anh em có chiến lược nào hay hoặc cần mình triển khai chi tiết thêm chiến lược nâng cao thì cứ comment nhé! :rocket: