old script
old script
0 at
https://mozilla.org/MPL/2.0/
// ©jdehorty
// @version=5
indicator('Machine Learning: Lorentzian Classification', 'Lorentzian
Classification', true, precision=4, max_labels_count=500)
import jdehorty/MLExtensions/2 as ml
import jdehorty/KernelFunctions/2 as kernels
type Settings
float source
int neighborsCount
int maxBarsBack
int featureCount
int colorCompression
bool showExits
bool useDynamicExits
type Label
int long
int short
int neutral
type FeatureArrays
array<float> f1
array<float> f2
array<float> f3
array<float> f4
array<float> f5
type FeatureSeries
float f1
float f2
float f3
float f4
float f5
type MLModel
int firstBarIndex
array<int> trainingLabels
int loopSize
float lastDistance
array<float> distancesArray
array<int> predictionsArray
int prediction
type FilterSettings
bool useVolatilityFilter
bool useRegimeFilter
bool useAdxFilter
float regimeThreshold
int adxThreshold
type Filter
bool volatility
bool regime
bool adx
// ==========================
// ==== Helper Functions ====
// ==========================
// ================
// ==== Inputs ====
// ================
// Label Object: Used for classifying historical data as training data for the ML
Model
Label direction =
Label.new(
long=1,
short=-1,
neutral=0
)
// EMA Settings
useEmaFilter = input.bool(title="Use EMA Filter", defval=false, group="Filters",
inline="ema")
emaPeriod = input.int(title="Period", defval=200, minval=1, step=1,
group="Filters", inline="ema", tooltip="The period of the EMA used for the EMA
Filter.")
isEmaUptrend = useEmaFilter ? close > ta.ema(close, emaPeriod) : true
isEmaDowntrend = useEmaFilter ? close < ta.ema(close, emaPeriod) : true
useSmaFilter = input.bool(title="Use SMA Filter", defval=false, group="Filters",
inline="sma")
smaPeriod = input.int(title="Period", defval=200, minval=1, step=1,
group="Filters", inline="sma", tooltip="The period of the SMA used for the SMA
Filter.")
isSmaUptrend = useSmaFilter ? close > ta.sma(close, smaPeriod) : true
isSmaDowntrend = useSmaFilter ? close < ta.sma(close, smaPeriod) : true
// Display Settings
showBarColors = input.bool(true, "Show Bar Colors", tooltip="Whether to show the
bar colors.", group="Display Settings")
showBarPredictions = input.bool(defval = true, title = "Show Bar Prediction
Values", tooltip = "Will show the ML model's evaluation of each bar as an
integer.", group="Display Settings")
useAtrOffset = input.bool(defval = false, title = "Use ATR Offset", tooltip = "Will
use the ATR offset instead of the bar prediction offset.", group="Display
Settings")
barPredictionsOffset = input.float(0, "Bar Prediction Offset", minval=0,
tooltip="The offset of the bar predictions as a percentage from the bar high or
close.", group="Display Settings")
// =================================
// ==== Next Bar Classification ====
// =================================
array.push(y_train_array, y_train_series)
// =========================
// ==== Core ML Logic ====
// =========================
// There are several problems with this traditional KNN approach in the context of
real-time calculations involving time series data:
// - It is computationally expensive to iterate through the entire dataset and
calculate the distance between every historical bar and
// the current bar.
// - Market time series data is often non-stationary, meaning that the statistical
properties of the data change slightly over time.
// - It is possible that the nearest neighbors are not the most informative ones,
and the KNN algorithm may return poor results if the
// nearest neighbors are not representative of the majority of the data.
// Of these two approaches, the latter is inherently limited by the fact that it
only considers the most recent bars in the overall dataset.
// The former approach has more potential to leverage historical price action, but
is limited by:
// - The possibility of a sudden "max" value throwing off the estimation
// - The possibility of selecting a set of approximate neighbors that are not
representative of the majority of the data by oversampling
// values that are not chronologically distinct enough from one another
// - The possibility of selecting too many "far" neighbors, which may result in a
poor estimation of price action
lastDistance = -1.0
size = math.min(settings.maxBarsBack-1, array.size(y_train_array)-1)
sizeLoop = math.min(settings.maxBarsBack-1, size)
// ============================
// ==== Prediction Filters ====
// ============================
// User Defined Filters: Used for adjusting the frequency of the ML Model's
predictions
filter_all = filter.volatility and filter.regime and filter.adx
// Filtered Signal: The model's prediction of future price movement direction with
user-defined filters applied
signal := prediction > 0 and filter_all ? direction.long : prediction < 0 and
filter_all ? direction.short : nz(signal[1])
// ===========================
// ==== Entries and Exits ====
// ===========================
// Dynamic Exit Conditions: Booleans for ML Model Position Exits based on Fractal
Filters and Kernel Regression Filters
lastSignalWasBullish = ta.barssince(startLongTrade) < ta.barssince(startShortTrade)
lastSignalWasBearish = ta.barssince(startShortTrade) < ta.barssince(startLongTrade)
barsSinceRedEntry = ta.barssince(startShortTrade)
barsSinceRedExit = ta.barssince(alertBullish)
barsSinceGreenEntry = ta.barssince(startLongTrade)
barsSinceGreenExit = ta.barssince(alertBearish)
isValidShortExit = barsSinceRedExit > barsSinceRedEntry
isValidLongExit = barsSinceGreenExit > barsSinceGreenEntry
endLongTradeDynamic = (isBearishChange and isValidLongExit[1])
endShortTradeDynamic = (isBullishChange and isValidShortExit[1])
// Fixed Exit Conditions: Booleans for ML Model Position Exits based on a Bar-Count
Filters
endLongTradeStrict = ((isHeldFourBars and isLastSignalBuy) or
(isHeldLessThanFourBars and isNewSellSignal and isLastSignalBuy)) and
startLongTrade[4]
endShortTradeStrict = ((isHeldFourBars and isLastSignalSell) or
(isHeldLessThanFourBars and isNewBuySignal and isLastSignalSell)) and
startShortTrade[4]
isDynamicExitValid = not useEmaFilter and not useSmaFilter and not
useKernelSmoothing
endLongTrade = settings.useDynamicExits and isDynamicExitValid ?
endLongTradeDynamic : endLongTradeStrict
endShortTrade = settings.useDynamicExits and isDynamicExitValid ?
endShortTradeDynamic : endShortTradeStrict
// =========================
// ==== Plotting Labels ====
// =========================
// Note: These will not repaint once the most recent bar has fully closed. By
default, signals appear over the last closed bar; to override this behavior set
offset=0.
plotshape(startLongTrade ? low : na, 'Buy', shape.labelup, location.belowbar,
color=color.new(#9c8700, 0), size=size.small, offset=0) // Changed green color
plotshape(startShortTrade ? high : na, 'Sell', shape.labeldown, location.abovebar,
color=color.new(#d9473d, 0), size=size.small, offset=0) // Changed red color
plotshape(endLongTrade and settings.showExits ? high : na, 'StopBuy', shape.xcross,
location.absolute, color=color.new(#9c8700, 0), size=size.tiny, offset=0) //
Changed green color
plotshape(endShortTrade and settings.showExits ? low : na, 'StopSell',
shape.xcross, location.absolute, color=color.new(#d9473d, 0), size=size.tiny,
offset=0) // Changed red color
// ================
// ==== Alerts ====
// ================
// =========================
// ==== Display Signals ====
// =========================
// =====================
// ==== Backtesting ====
// =====================
// The following can be used to display real-time trade stats. This can be a useful
mechanism for obtaining real-time feedback during Feature Engineering. This does
NOT replace the need to properly backtest.
// Note: In this context, a "Stop-Loss" is defined instances where the ML Signal
prematurely flips directions before an exit signal can be generated.
[totalWins, totalLosses, totalEarlySignalFlips, totalTrades, tradeStatsHeader,
winLossRatio, winRate] = ml.backtest(high, low, open, startLongTrade, endLongTrade,
startShortTrade, endShortTrade, isEarlySignalFlip, maxBarsBackIndex, bar_index,
settings.source, useWorstCase)
init_table() =>
c_transparent = color.new(color.black, 100)
table.new(position.top_right, columns=2, rows=7,
frame_color=color.new(color.black, 100), frame_width=1, border_width=1,
border_color=c_transparent)
if showTradeStats
var tbl = ml.init_table()
if barstate.islast
update_table(tbl, tradeStatsHeader, totalTrades, totalWins, totalLosses,
winLossRatio, winRate, totalEarlySignalFlips)