# Maximizing Profits with a KST-Based Python Trading Strategy

Written on

The KST (Know Sure Thing) indicator serves as a momentum oscillator aimed at pinpointing significant market trend shifts. Created by Martin J. Pring, it operates on four distinct time frames: 10, 15, 20, and 30 periods.

Calculation of the KST indicator involves a weighted moving average of the rate of change (ROC) values across these four time frames. The ROC quantifies the percentage change in price over a designated time. This KST value is subsequently refined through another moving average.

Traders can derive buy and sell signals from the KST indicator by observing the interaction between the KST line and a signal line. A KST line crossing above the signal line signals a buying opportunity, while a crossing below indicates a selling point.

Backtesting is essential for validating any trading strategy, enabling traders to analyze performance against historical data. This article will guide you through backtesting a trading strategy utilizing the KST indicator.

To kick off, we will import historical price data for the asset of interest and compute the KST indicator using the following code:

def calculate_KST(df, roc_periods=(10, 15, 20, 30), sma_periods=(10, 10, 10, 15)):

for i, r in enumerate(roc_periods):

df[f'ROC{i+1}'] = ((df['Close'] - df['Close'].shift(r)) / df['Close'].shift(r)) * 100weights = [1, 2, 3, 4]

for i, s in enumerate(sma_periods):

df[f'WMA{i+1}'] = df[[f'ROC{j+1}' for j in range(i+1)]] @ weights[:i+1] / sum(weights[:i+1])df['KST'] = df[[f'WMA{j+1}' for j in range(4)]] @ weights / sum(weights)

df['KSTS'] = df['KST'].rolling(window=9).mean()

return df

This function receives a DataFrame df containing historical price data, as well as two tuplesâ€”roc_periods and sma_periodsâ€”indicating the periods for ROC and moving average calculations. It returns the DataFrame augmented with columns for each of the four ROC values, the four weighted moving averages, KST, and KSTS (the smoothed KST).

df['KST'] = ta.trend.kst(close=df['close'], roc1=10, roc2=15, roc3=20, roc4=30, window1=10, window2=10, window3=10, window4=15, fillna=True)

df['KSTS'] = ta.trend.kst_sig(close=df['close'], roc1=10, roc2=15, roc3=20, roc4=30, window1=10, window2=10, window3=10, window4=15, nsig=9, fillna=True)

Next, we will establish our trading strategy that will generate buy signals when the KST line surpasses the KSTS line and sell signals when the KST line dips below the KSTS line. The code for this is as follows:

def generate_signals(df):

signals = []

for i in range(1, len(df)-1):

if df.iloc[i]['KSTS'] > df.iloc[i]['KST']:

signals.append(-1)elif df.iloc[i]['KST'] > df.iloc[i]['KSTS']:

signals.append(1)else:

signals.append(0)return signals

This function processes the DataFrame with KST and KSTS columns, generating a list of signals based on the crossings of these two lines.

Finally, we will conduct a backtest of our strategy using the historical price data and the signals generated. We will start with an initial capital of $1,000 and simulate trades with the following code:

## Backtesting with ETHUSDT on a 1-Hour Timeframe

# Add the signals list to the dataframe as a new column

df["signal"] = signals

print(signals)

investment = 1000

current_investment = 1000

invested_amount = 0

fees = 0

profit = 0

is_invested = False

best_trade = -99999999

worst_trade = 99999999

largest_loss = 0

largest_gain = 0

total_trades = 0

loss_threshold = 0.03 # Stop loss threshold at 3%

loss_amount = 0 # variable to track loss amount

for i in range(500, len(df)):

signal = df.iloc[i]['signal']

close = df.iloc[i]['close']

if signal == 1 and not is_invested:

entry_point = close

print(signal)

print(close)

quantity = (current_investment / close)

print(current_investment)

print(quantity)

invested_amount = quantity * close

is_invested = True

elif signal == -1 and is_invested:

print(signal)

print('close', close)

profit = quantity * (close - entry_point) - 2

print('profit', profit)

current_investment += profit

invested_amount = 0

total_trades += 1

is_invested = False

if profit > largest_gain:

largest_gain = profitif profit < largest_loss:

largest_loss = profitif profit > best_trade:

best_trade = profitif profit < worst_trade:

worst_trade = profitloss_amount = 0 # reset loss_amount when position is closed

elif is_invested:

# check if stop loss threshold has been reached

loss = invested_amount - (quantity * close)

if loss > invested_amount * loss_threshold:

print("Stop loss triggered!")

profit = invested_amount * (1 - loss_threshold) - invested_amount - 2

current_investment += profit

invested_amount = 0

total_trades += 1

is_invested = False

if profit > largest_gain:

largest_gain = profitif profit < largest_loss:

largest_loss = profitif profit > best_trade:

best_trade = profitif profit < worst_trade:

worst_trade = profitloss_amount = 0 # reset loss_amount when position is closed

else:

loss_amount = loss # update loss_amountelse:

passfinal_profit = current_investment - investment

print("Final Profit: ", final_profit)

print("Best Trade: ", best_trade)

print("Worst Trade: ", worst_trade)

print("Largest Loss: ", largest_loss)

print("Largest Gain: ", largest_gain)

print("Total Trades: ", total_trades)

After executing the backtest on the historical data, the final portfolio value was $1,218,449. This outcome highlights the strategy's profitability, suggesting its potential for generating positive returns in future trades.