| Overall Statistics |
|
Total Trades 54 Average Win 0.45% Average Loss -0.56% Compounding Annual Return 6.007% Drawdown 14.100% Expectancy 0.330 Net Profit 33.919% Sharpe Ratio 0.827 Probabilistic Sharpe Ratio 31.798% Loss Rate 26% Win Rate 74% Profit-Loss Ratio 0.80 Alpha 0.052 Beta -0.009 Annual Standard Deviation 0.062 Annual Variance 0.004 Information Ratio -0.437 Tracking Error 0.183 Treynor Ratio -5.911 Total Fees $93.48 Estimated Strategy Capacity $200000000.00 Lowest Capacity Asset DBC TFVSB03UY0DH |
from Model import Model
class SmoothVioletCoyote(QCAlgorithm):
def Initialize(self):
self.SetStartDate(2015, 10, 1) # Set Start Date
self.SetEndDate(2020, 10, 1)
self.SetCash(100000) # Set Strategy Cash
tickers = ['VTI', 'AGG', 'DBC', 'VIXY']
for ticker in tickers:
self.AddEquity(ticker, Resolution.Daily)
n_periods = 51
self.data = RollingWindow[Slice](n_periods)
self.Train(self.DateRules.MonthStart('VTI'), self.TimeRules.Midnight, self.Rebalance)
self.model = None
self.SetWarmup(n_periods)
self.prev_day = -1
def OnData(self, data):
# this prevents duplicate bars that sometimes occurs because of SetWarmUp
if self.prev_day != self.Time.day:
self.data.Add(data)
self.prev_day = self.Time.day
def Rebalance(self):
if not self.data.IsReady:
return
try:
# since RollingWindow is recent at top, we need to reverse it
data = self.PandasConverter.GetDataFrame(self.data).iloc[::-1]
except:
return
# turn the closing prices for each equity into columns
data = data['close'].unstack(level=0)
# sometimes we are missing a row of data
if len(data) < self.data.Count:
return
tickers = [symbol.split(' ')[0] for symbol in data.columns]
if self.model is None:
self.model = Model()
allocations = self.model.get_allocations(data)
self.Log(f'Portfolio Allocations: {allocations}')
for ticker, allocation in zip(tickers, allocations):
self.SetHoldings(ticker, allocation)import tensorflow as tf
from tensorflow.keras.layers import LSTM, Flatten, Dense
from tensorflow.keras.models import Sequential
import tensorflow.keras.backend as K
class Model:
def __init__(self):
self.data = None
self.model = None
def __build_model(self, input_shape, outputs):
model = Sequential([
LSTM(64, input_shape=input_shape),
Flatten(),
Dense(outputs, activation='softmax')
])
def sharpe_loss(_, y_pred):
data = tf.divide(self.data, self.data[0])
portfolio_values = tf.reduce_sum(tf.multiply(data, y_pred), axis=1)
portfolio_returns = (portfolio_values[1:] - portfolio_values[:-1]) / portfolio_values[:-1]
sharpe = K.mean(portfolio_returns) / K.std(portfolio_returns)
return -sharpe
model.compile(loss=sharpe_loss, optimizer='adam')
return model
def get_allocations(self, data):
data_w_ret = np.concatenate([ data.values[1:], data.pct_change().values[1:] ], axis=1)
data = data.iloc[1:]
self.data = tf.cast(tf.constant(data), float)
if self.model is None:
self.model = self.__build_model(data_w_ret.shape, len(data.columns))
fit_predict_data = data_w_ret[np.newaxis,:]
self.model.fit(fit_predict_data, np.zeros((1, len(data.columns))), epochs=30, shuffle=False)
return self.model.predict(fit_predict_data)[0]