LSTM Trading Strategy

LSTM Trading Strategy (机器学习交易策略)

以下代码由ChatGPT生成一个模版,做了不少的改动。
需要配置一下数据,可以从其他网站下载任意感兴趣的相关交易数据。
配置 USER_HOME 这个环境变量,指向你的数据目录,或者任意的本地工作目录。
为节省测试时间:1)总是选取后1500个数据点,可以改,但是注意不要太小,否则不够切分;2)神经网络选了小的,可以增大到比如512,但是速度会更慢。

fit 里的 epochs, batch_size 和 MinMaxScalar(0,1) 都是很关键的基础配置项。

import datetime,random,os
import numpy as np
import pandas as pd
import talib
import tensorflow as tf
from sklearn.preprocessing import MinMaxScaler
from tensorflow.keras.models import Sequential
from tensorflow.keras.layers import LSTM, Dense, Dropout
from tensorflow.keras.callbacks import ModelCheckpoint,EarlyStopping

import matplotlib.pyplot as plt

tf.keras.backend.clear_session()

class LossHistory(tf.keras.callbacks.Callback):
    def on_train_begin(self, logs={}):
        self.losses = []
        #self.val_losses = []

    def on_batch_end(self, batch, logs={}):
        self.losses.append(logs.get('loss'))
        #self.val_losses.append(logs.get('val_loss'))

def download_data():
    #df = pd.read_csv("data/^GSPC_short.csv")
    
    df = pd.read_csv(os.getenv('USER_HOME','') + '/tmp/btc-usdt_1D.csv')
    df = df[-1500:]
    df['Adj Close'] = df['close'].astype(float)
    df['direction'] = df['close'].astype(float)-df['open'].astype(float)
    df['direction'] = df['direction'].apply(lambda v: 1 if v>0 else -1 if v<0 else 0)
    df['Volume'] = (df['volume'] * df['direction']).astype(float)
    df['Returns'] = (df['close'] - df['open'])/df['open']

    df['macd'] = talib.EMA(df['Adj Close'].astype(float), timeperiod=7) \
                    - talib.EMA(df['Adj Close'].astype(float), timeperiod=14)


    data = df.copy()
    data = data.dropna()
    
    ts = data[['timestamp']]
    return data[['Adj Close','Volume', 'macd']],ts

data, ts = download_data()
print( data )
scaler = MinMaxScaler(feature_range=(0, 1))
scaled_data = scaler.fit_transform(data)
y_scaler =  MinMaxScaler(feature_range=(0, 1))
_ = y_scaler.fit_transform( data[['Adj Close']])

# Split the data into training and testing sets
train_size = int(len(scaled_data) * 0.7)
train_data, test_data = scaled_data[:train_size], scaled_data[train_size:]
test_ts = ts.to_numpy()[train_size:]

def create_dataset(dataset, time_step=1):
    dataX, dataY = [], []
    for i in range(len(dataset) - time_step - 1):
        a = dataset[i:(i + time_step), :]   # All columns, i.e., features are considered
        dataX.append(a)
        dataY.append(dataset[i + time_step, 0]) # Only the "Adj Close" needs to be fitted
    return np.array(dataX), np.array(dataY)

time_step = 120*2

X_train, y_train = create_dataset(train_data, time_step)
X_test, y_test = create_dataset(test_data, time_step)
nfeatures = data.shape[1]

assert X_train.shape[1] == time_step, 'Should be equal.'

# Reshape input to be [samples, time steps, features]
X_train = X_train.reshape(X_train.shape[0], X_train.shape[1], nfeatures)
X_test = X_test.reshape(X_test.shape[0], X_test.shape[1], nfeatures)

cbs = [
    LossHistory(),
    ModelCheckpoint("foobar", monitor='loss', verbose=0, save_best_only=True, mode='min'),
    EarlyStopping(patience=3)     
]  
l1 = 51 # 512 # Larger is slower!
l2 = 50 # 256
name = f"{int(datetime.datetime.utcnow().timestamp())}{random.randint(1,10)}"
model = Sequential(name=name)
model.add(LSTM(l1, return_sequences=True, input_shape=(time_step, nfeatures)))
model.add(Dropout(0.2))
model.add(LSTM(l2, stateful=False, recurrent_dropout=0.01 )) #kernel_regularizer=regularizers.l2(0.001), 
model.add(Dropout(0.2))
#model.add(Dense(32))
model.add(Dense(1,activation='relu'))
model.compile(optimizer='adam', loss='mean_squared_error')
model.fit(X_train, y_train, 
            #callbacks=cbs,
            validation_split=0 if len(X_train) < 10 else 0.3,
            batch_size=1, 
            epochs=1
            )

train_predict = model.predict(X_train)
test_predict = model.predict(X_test)

def trading_strategy(test_data, test_predict, threshold=0.01):
    signal = []
    for i in range(len(test_predict)):
        mkt_pce = test_data[i, 0] # Assumed: 1st colume is the Adj Close!!
        if test_predict[i] > mkt_pce * (1 + threshold):
            signal.append(1)  # Buy
        elif test_predict[i] < mkt_pce * (1 - threshold):
            signal.append(-1)  # Sell
        else:
            signal.append(0)  # Hold
    return signal

signals = trading_strategy(test_data[time_step:], test_predict)

# Transform back to original scale
train_predict = y_scaler.inverse_transform(train_predict)
y_train = y_scaler.inverse_transform([y_train])
test_predict = y_scaler.inverse_transform(test_predict)
y_test = y_scaler.inverse_transform([y_test])
test_data = scaler.inverse_transform(test_data)

plt.figure(figsize=(16,8))
plt.plot(data.index, data['Adj Close'], label='Actual Price')
plt.plot(data.index[time_step+1: train_size], train_predict, label='Train Predict')
plt.plot(data.index[len(train_predict)+(time_step*2)+1:len(data)-1], test_predict, label='Test Predict')
plt.legend()
plt.title(f'Look back window width: {time_step}')

fn = os.getenv("USER_HOME",'') + '/tmp/chatgpt_lstm.png'
plt.savefig( fn )
print( 'saved:',fn)

initial_balance = 1_000_000
def backtest_strategy(data, signals, test_ts):
    balance = initial_balance
    shares = 0
    
    for i in range(len(signals)):
        pce = data[i + time_step, 0] # Assumed the first column is the Adj Close price.
        if signals[i] == 1 and balance >= pce:  # Buy
            shares = balance // pce
            balance -= shares * pce
            print(f'-- [ {test_ts[i]} ] buy ${pce} (+{shares})' )
        elif signals[i] == -1 and shares > 0:  # Sell
            balance += shares * pce
            print(f'  -- [ {test_ts[i]} ] sell ${pce} (-{shares})' )
            shares = 0  

    # Mark-to-market, final portfolio balance
    if shares > 0:
        balance += shares * data[-1, 0]

    return balance

final_balance = backtest_strategy(test_data, signals,test_ts)
rtn = (final_balance-initial_balance)/initial_balance*100
print(f"{test_ts[0][0]} ~ {test_ts[-1][0]}")
print(f"Final balance: {final_balance:,.2f}, {rtn:.1f}%")

  • 3
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值