Fractal Trail [UAlgo]

The Fractal Trail [UAlgo] is designed to identify and utilize Williams fractals as dynamic trailing stops. This tool serves traders by marking key fractal points on the chart and leveraging them to create adaptive stop-loss trails, enhancing risk management and trade decision-making.

Williams fractals are pivotal in identifying potential reversals and critical support/resistance levels. By plotting fractals dynamically and providing configurable options, this indicator allows for personalized adjustments based on the trader’s strategy.

This script integrates both visual fractal markers and adjustable trailing stops, offering insights into market trends while catering to a wide variety of trading styles and timeframes.

``` // This Pine Script™ code is subject to the terms of the Attribution-NonCommercial-ShareAlike 4.0 International (CC BY-NC-SA 4.0) https://creativecommons.org/licenses/by-nc-sa/4.0/
// © UAlgo
//@version=5
indicator(title='Fractal Trail [UAlgo]', shorttitle='Fractal Trail [UAlgo]', overlay=true)

bool showWilliamsFractals = input.bool(defval=true, title='Show All Williams Fractals', group="Fractal Trail [UAlgo]", tooltip='Places triangle indicators to mark each Williams fractal position')
bool showWilliamsStops = input.bool(defval=true, title='Show Williams Trail', tooltip='Creates a dynamic trailing stop based on the latest identified fractal', group="Fractal Trail [UAlgo]")

int williamsLeftRange = input.int(defval=2, minval=1, maxval=50, title='Williams Fractal Range Left/Right', group="Fractal Trail [UAlgo]", tooltip='Specifies the number of previous bars to analyze for the range. Standard setting is two bars in both directions.',inline = "range")
int williamsRightRange = input.int(defval=2, minval=1, maxval=50, title=' ', tooltip='Determines the number of forward bars to examine for the range. Best practice suggests matching the backward range value.', group="Fractal Trail [UAlgo]",inline = "range")
float williamsStopBuffer = input.float(defval=0, minval=0, maxval=20, step=0.5, title='Trail Buffer Percent (%)', group="Fractal Trail [UAlgo]", tooltip='Sets a percentage-based safety margin between the fractal price and the trailing stop')
string flipInput = input.string(title='Trail Invalidation Source', defval='Close', options=['Close', 'Wick'], tooltip='Determines whether the trail flips based on candle closing prices or extreme points (high/low)', group="Fractal Trail [UAlgo]")
color upTrailColor = input.color(defval=color.rgb(40, 218, 150), title='Up/Down Trail Color', group="Fractal Trail [UAlgo]",inline = "trailC")
color dnTrailColor = input.color(defval=color.new(color.blue, 0), title=' ', group="Fractal Trail [UAlgo]",inline = "trailC")
color fillColorUp = color.new(color.rgb(40, 218, 150), 80)
color fillColorDn = color.new(color.blue, 80)

f_IsWilliamsFractal(_leftRange, _rightRange, _type) =>
    _isFractal = _type == 'high' and high[_rightRange] >= ta.highest(high, _leftRange + _rightRange + 1) or _type == 'low' and low[_rightRange] <= ta.lowest(low, _leftRange + _rightRange + 1)
    _fractalValue = _isFractal and _type == 'high' ? high[_rightRange] : _isFractal and _type == 'low' ? low[_rightRange] : na
    [_isFractal, _fractalValue]

[isHighFractal, highFractalPrice] = f_IsWilliamsFractal(williamsLeftRange, williamsRightRange, 'high')
[isLowFractal, lowFractalPrice] = f_IsWilliamsFractal(williamsLeftRange, williamsRightRange, 'low')

isHighFractal := isHighFractal[1] ? false : isHighFractal
isLowFractal := isLowFractal[1] ? false : isLowFractal

highFractalOffset = 0 - williamsRightRange
color white10 = color.new(color.white, 10)
plotshape(isHighFractal and showWilliamsFractals, title='Shape for Williams High', style=shape.triangledown, location=location.abovebar, color=white10, size=size.tiny, offset=highFractalOffset)
lowFractalOffset = 0 - williamsRightRange
plotshape(isLowFractal and showWilliamsFractals, title='Shape for Williams Low', style=shape.triangleup, location=location.belowbar, color=white10, size=size.tiny, offset=lowFractalOffset)

f_addPercentBuffer(_input, _buffer, _direction) =>
    _direction == 'plus' ? _input * (1 + _buffer / 100) : _direction == 'minus' ? _input * (1 - _buffer / 100) : na

lowFractalPriceBuffered = f_addPercentBuffer(lowFractalPrice, williamsStopBuffer, 'minus')
highFractalPriceBuffered = f_addPercentBuffer(highFractalPrice, williamsStopBuffer, 'plus')

f_persistAndReset(_trigger, _source) =>
    var float _output = 0.0
    _output := _trigger ? _source : _output[1]
    _output

longStopPrice = f_persistAndReset(isLowFractal, lowFractalPriceBuffered)
shortStopPrice = f_persistAndReset(isHighFractal, highFractalPriceBuffered)

f_trail(_source, _trail, _direction) =>
    _direction == 'down' and _source >= _trail[1] ? _trail : _direction == 'up' and _source <= _trail[1] ? _trail : _source

var float longStopPriceTrail = longStopPrice
var float shortStopPriceTrail = shortStopPrice

shortStopPriceTrail := f_trail(shortStopPrice, shortStopPriceTrail, 'down')
longStopPriceTrail := f_trail(longStopPrice, longStopPriceTrail, 'up')

f_flip(_flipInput, _longTrail, _shortTrail, _longReset, _shortReset) =>
    var bool _flipLongNow = false
    var bool _flipShortNow = false
    var bool _isLong = true
    var bool _isShort = true
    float _flipLongSource = _flipInput == 'Close' ? close : _flipInput == 'Wick' ? high : na
    float _flipShortSource = _flipInput == 'Close' ? close : _flipInput == 'Wick' ? low : na
    _flipLongNow := _isShort[1] and _flipLongSource > _shortTrail ? true : false
    _flipShortNow := _isLong[1] and _flipShortSource < _longTrail ? true : false
    _flipLongNow := _flipShortNow and _flipLongNow and close > _longTrail ? true : _flipShortNow and _flipLongNow and close <= _longTrail ? false : _flipLongNow
    _flipShortNow := _flipLongNow and _flipShortNow and close < _shortTrail ? true : _flipShortNow and _flipLongNow and close >= _shortTrail ? false : _flipShortNow
    _isLong := _flipLongNow ? true : _flipShortNow ? false : na(_isLong[1]) ? true : _isLong[1]
    _isShort := _flipShortNow ? true : _flipLongNow ? false : na(_isShort[1]) ? true : _isShort[1]
    _longTrailOutput = _longTrail
    _shortTrailOutput = _shortTrail
    _longTrailOutput := _isLong and not _isLong[1] ? _longReset : _longTrailOutput
    _shortTrailOutput := _isShort and not _isShort[1] ? _shortReset : _shortTrailOutput
    float _longTrailPlot = _isLong ? _longTrailOutput : _isLong and _isShort ? _longTrailOutput : na
    float _shortTrailPlot = _isShort ? _shortTrailOutput : _isLong and _isShort ? _shortTrailOutput : na
    [_longTrailOutput, _shortTrailOutput, _longTrailPlot, _shortTrailPlot]

f_getFlipResetWilliamsLong(_longTrail, _buffer, _isLowFractal, _rightRange) =>
    _barIndexWhenLastFractalConfirmed = ta.valuewhen(_isLowFractal, bar_index, 0)
    _barsSinceLastFractalConfirmed = bar_index - _barIndexWhenLastFractalConfirmed
    int _barsToGoBack = _barsSinceLastFractalConfirmed + _rightRange
    float _lowestLow = low
    for i = 0 to _barsToGoBack by 1
        _lowestLow := math.min(low[i], _lowestLow)
        _lowestLow
    _lowestLowAdjusted = _lowestLow * (1 - _buffer / 100)
    _lowestLowAdjusted

f_getFlipResetWilliamsShort(_shortTrail, _buffer, _isHighFractal, _leftRange) =>
    _barIndexWhenLastFractalConfirmed = ta.valuewhen(_isHighFractal, bar_index, 0)
    _barsSinceLastFractalConfirmed = bar_index - _barIndexWhenLastFractalConfirmed
    int _barsToGoBack = _barsSinceLastFractalConfirmed + _leftRange
    float _highestHigh = high
    for i = 0 to _barsToGoBack by 1
        _highestHigh := math.max(high[i], _highestHigh)
        _highestHigh
    _highestHighAdjusted = _highestHigh * (1 + _buffer / 100)
    _highestHighAdjusted

longStopPrice := f_getFlipResetWilliamsLong(longStopPrice, williamsStopBuffer, isLowFractal, williamsRightRange)
shortStopPrice := f_getFlipResetWilliamsShort(shortStopPrice, williamsStopBuffer, isHighFractal, williamsLeftRange)

[longStopPriceTrailTemp, shortStopPriceTrailTemp, longStopPriceTrailPlot, shortStopPriceTrailPlot] = f_flip(flipInput, longStopPriceTrail, shortStopPriceTrail, longStopPrice, shortStopPrice)

longStopPriceTrail := longStopPriceTrailTemp
shortStopPriceTrail := shortStopPriceTrailTemp

shortStopPriceTrailPlotDisplay = showWilliamsStops ? shortStopPriceTrailPlot : na
longStopPriceTrailPlotDisplay = showWilliamsStops ? longStopPriceTrailPlot : na

plot(shortStopPriceTrailPlotDisplay, 'Williams Trailing Stop High Price', color=dnTrailColor, style=plot.style_linebr, linewidth=3)
plot(shortStopPriceTrailPlotDisplay, 'Williams Trailing Stop High Price Highlight', color=dnTrailColor, style=plot.style_linebr, linewidth=1)
plot(longStopPriceTrailPlotDisplay, 'Williams Trailing Stop Low Price', color=upTrailColor, style=plot.style_linebr, linewidth=3)
plot(longStopPriceTrailPlotDisplay, 'Williams Trailing Stop Low Price Highlight', color=upTrailColor, style=plot.style_linebr, linewidth=1)

pricefill = plot(hl2, 'Midline', color=color.gray, display=display.none)

isUptrend = close < shortStopPriceTrailPlotDisplay
isDowntrend = close > longStopPriceTrailPlotDisplay

fillColor = isUptrend ? color.new(fillColorDn, 85) : color.new(fillColorUp, 85)
fillPrice = isUptrend ? shortStopPriceTrailPlotDisplay : longStopPriceTrailPlotDisplay

fill(plot(fillPrice, title='Williams Trailing Stop Fill High'), pricefill, color=fillColor)

alertCrossLongStop = na(longStopPriceTrailPlot) and not na(longStopPriceTrailPlot[1])
alertCrossShortStop = na(shortStopPriceTrailPlot) and not na(shortStopPriceTrailPlot[1])
alertcondition(alertCrossLongStop, title='Crossed Williams Long Stop', message='Alert from Williams Fractal Trailing Stops: \n {{ticker}} price crossed long stop')
alertcondition(alertCrossShortStop, title='Crossed Williams Short Stop', message='Alert from Williams Fractal Trailing Stops: \n {{ticker}} price crossed short stop')

alertcondition(isHighFractal, title='High Printed', message='Alert from Williams Fractal Trailing Stops: \n {{ticker}} Williams High has been confirmed')
alertcondition(isLowFractal, title='Low Printed', message='Alert from Williams Fractal Trailing Stops: \n {{ticker}} Williams Low has been confirmed') ``` study this code
// This Pine Script™ code is subject to the terms of the Attribution-NonCommercial-ShareAlike 4.0 International (CC BY-NC-SA 4.0) https://creativecommons.org/licenses/by-nc-sa/4.0/
// © UAlgo
//@version=5
indicator(title='Fractal Trail [UAlgo]', shorttitle='Fractal Trail [UAlgo]', overlay=true)

bool showWilliamsFractals = input.bool(defval=true, title='Show All Williams Fractals', group="Fractal Trail [UAlgo]", tooltip='Places triangle indicators to mark each Williams fractal position')
bool showWilliamsStops = input.bool(defval=true, title='Show Williams Trail', tooltip='Creates a dynamic trailing stop based on the latest identified fractal', group="Fractal Trail [UAlgo]")
int williamsLeftRange = input.int(defval=2, minval=1, maxval=50, title='Williams Fractal Range Left/Right', group="Fractal Trail [UAlgo]", tooltip='Specifies the number of previous bars to analyze for the range. Standard setting is two bars in both directions.',inline = "range")
int williamsRightRange = input.int(defval=2, minval=1, maxval=50, title=' ', tooltip='Determines the number of forward bars to examine for the range. Best practice suggests matching the backward range value.', group="Fractal Trail [UAlgo]",inline = "range")
float williamsStopBuffer = input.float(defval=0, minval=0, maxval=20, step=0.5, title='Trail Buffer Percent (%)', group="Fractal Trail [UAlgo]", tooltip='Sets a percentage-based safety margin between the fractal price and the trailing stop')
string flipInput = input.string(title='Trail Invalidation Source', defval='Close', options=['Close', 'Wick'], tooltip='Determines whether the trail flips based on candle closing prices or extreme points (high/low)', group="Fractal Trail [UAlgo]")
color upTrailColor = input.color(defval=color.rgb(40, 218, 150), title='Up/Down Trail Color', group="Fractal Trail [UAlgo]",inline = "trailC")
color dnTrailColor = input.color(defval=color.new(color.blue, 0), title=' ', group="Fractal Trail [UAlgo]",inline = "trailC")
color fillColorUp = color.new(color.rgb(40, 218, 150), 80)
color fillColorDn = color.new(color.blue, 80)

f_IsWilliamsFractal(_leftRange, _rightRange, _type) =>
    _isFractal = _type == 'high' and high[_rightRange] >= ta.highest(high, _leftRange + _rightRange + 1) or _type == 'low' and low[_rightRange] <= ta.lowest(low, _leftRange + _rightRange + 1)
    _fractalValue = _isFractal and _type == 'high' ? high[_rightRange] : _isFractal and _type == 'low' ? low[_rightRange] : na
    [_isFractal, _fractalValue]

[isHighFractal, highFractalPrice] = f_IsWilliamsFractal(williamsLeftRange, williamsRightRange, 'high')
[isLowFractal, lowFractalPrice] = f_IsWilliamsFractal(williamsLeftRange, williamsRightRange, 'low')

isHighFractal := isHighFractal[1] ? false : isHighFractal
isLowFractal := isLowFractal[1] ? false : isLowFractal

highFractalOffset = 0 - williamsRightRange
color white10 = color.new(color.white, 10)
plotshape(isHighFractal and showWilliamsFractals, title='Shape for Williams High', style=shape.triangledown, location=location.abovebar, color=white10, size=size.tiny, offset=highFractalOffset)
lowFractalOffset = 0 - williamsRightRange
plotshape(isLowFractal and showWilliamsFractals, title='Shape for Williams Low', style=shape.triangleup, location=location.belowbar, color=white10, size=size.tiny, offset=lowFractalOffset)

f_addPercentBuffer(_input, _buffer, _direction) =>
    _direction == 'plus' ? _input * (1 + _buffer / 100) : _direction == 'minus' ? _input * (1 - _buffer / 100) : na

lowFractalPriceBuffered = f_addPercentBuffer(lowFractalPrice, williamsStopBuffer, 'minus')
highFractalPriceBuffered = f_addPercentBuffer(highFractalPrice, williamsStopBuffer, 'plus')

f_persistAndReset(_trigger, _source) =>
    var float _output = 0.0
    _output := _trigger ? _source : _output[1]
    _output

longStopPrice = f_persistAndReset(isLowFractal, lowFractalPriceBuffered)
shortStopPrice = f_persistAndReset(isHighFractal, highFractalPriceBuffered)

f_trail(_source, _trail, _direction) =>
    _direction == 'down' and _source >= _trail[1] ? _trail : _direction == 'up' and _source <= _trail[1] ? _trail : _source

var float longStopPriceTrail = longStopPrice
var float shortStopPriceTrail = shortStopPrice

shortStopPriceTrail := f_trail(shortStopPrice, shortStopPriceTrail, 'down')
longStopPriceTrail := f_trail(longStopPrice, longStopPriceTrail, 'up')

f_flip(_flipInput, _longTrail, _shortTrail, _longReset, _shortReset) =>
    var bool _flipLongNow = false
    var bool _flipShortNow = false
    var bool _isLong = true
    var bool _isShort = true
    float _flipLongSource = _flipInput == 'Close' ? close : _flipInput == 'Wick' ? high : na
    float _flipShortSource = _flipInput == 'Close' ? close : _flipInput == 'Wick' ? low : na
    _flipLongNow := _isShort[1] and _flipLongSource > _shortTrail ? true : false
    _flipShortNow := _isLong[1] and _flipShortSource < _longTrail ? true : false
    _flipLongNow := _flipShortNow and _flipLongNow and close > _longTrail ? true : _flipShortNow and _flipLongNow and close <= _longTrail ? false : _flipLongNow
    _flipShortNow := _flipLongNow and _flipShortNow and close < _shortTrail ? true : _flipShortNow and _flipLongNow and close >= _shortTrail ? false : _flipShortNow
    _isLong := _flipLongNow ? true : _flipShortNow ? false : na(_isLong[1]) ? true : _isLong[1]
    _isShort := _flipShortNow ? true : _flipLongNow ? false : na(_isShort[1]) ? true : _isShort[1]
    _longTrailOutput = _longTrail
    _shortTrailOutput = _shortTrail
    _longTrailOutput := _isLong and not _isLong[1] ? _longReset : _longTrailOutput
    _shortTrailOutput := _isShort and not _isShort[1] ? _shortReset : _shortTrailOutput
    float _longTrailPlot = _isLong ? _longTrailOutput : _isLong and _isShort ? _longTrailOutput : na
    float _shortTrailPlot = _isShort ? _shortTrailOutput : _isLong and _isShort ? _shortTrailOutput : na
    [_longTrailOutput, _shortTrailOutput, _longTrailPlot, _shortTrailPlot]

f_getFlipResetWilliamsLong(_longTrail, _buffer, _isLowFractal, _rightRange) =>
    _barIndexWhenLastFractalConfirmed = ta.valuewhen(_isLowFractal, bar_index, 0)
    _barsSinceLastFractalConfirmed = bar_index - _barIndexWhenLastFractalConfirmed
    int _barsToGoBack = _barsSinceLastFractalConfirmed + _rightRange
    float _lowestLow = low
    for i = 0 to _barsToGoBack by 1
        _lowestLow := math.min(low[i], _lowestLow)
        _lowestLow
    _lowestLowAdjusted = _lowestLow * (1 - _buffer / 100)
    _lowestLowAdjusted

f_getFlipResetWilliamsShort(_shortTrail, _buffer, _isHighFractal, _leftRange) =>
    _barIndexWhenLastFractalConfirmed = ta.valuewhen(_isHighFractal, bar_index, 0)
    _barsSinceLastFractalConfirmed = bar_index - _barIndexWhenLastFractalConfirmed
    int _barsToGoBack = _barsSinceLastFractalConfirmed + _leftRange
    float _highestHigh = high
    for i = 0 to _barsToGoBack by 1
        _highestHigh := math.max(high[i], _highestHigh)
        _highestHigh
    _highestHighAdjusted = _highestHigh * (1 + _buffer / 100)
    _highestHighAdjusted

longStopPrice := f_getFlipResetWilliamsLong(longStopPrice, williamsStopBuffer, isLowFractal, williamsRightRange)
shortStopPrice := f_getFlipResetWilliamsShort(shortStopPrice, williamsStopBuffer, isHighFractal, williamsLeftRange)

[longStopPriceTrailTemp, shortStopPriceTrailTemp, longStopPriceTrailPlot, shortStopPriceTrailPlot] = f_flip(flipInput, longStopPriceTrail, shortStopPriceTrail, longStopPrice, shortStopPrice)

longStopPriceTrail := longStopPriceTrailTemp
shortStopPriceTrail := shortStopPriceTrailTemp

shortStopPriceTrailPlotDisplay = showWilliamsStops ? shortStopPriceTrailPlot : na
longStopPriceTrailPlotDisplay = showWilliamsStops ? longStopPriceTrailPlot : na

plot(shortStopPriceTrailPlotDisplay, 'Williams Trailing Stop High Price', color=dnTrailColor, style=plot.style_linebr, linewidth=3)
plot(shortStopPriceTrailPlotDisplay, 'Williams Trailing Stop High Price Highlight', color=dnTrailColor, style=plot.style_linebr, linewidth=1)
plot(longStopPriceTrailPlotDisplay, 'Williams Trailing Stop Low Price', color=upTrailColor, style=plot.style_linebr, linewidth=3)
plot(longStopPriceTrailPlotDisplay, 'Williams Trailing Stop Low Price Highlight', color=upTrailColor, style=plot.style_linebr, linewidth=1)

pricefill = plot(hl2, 'Midline', color=color.gray, display=display.none)

isUptrend = close < shortStopPriceTrailPlotDisplay
isDowntrend = close > longStopPriceTrailPlotDisplay

fillColor = isUptrend ? color.new(fillColorDn, 85) : color.new(fillColorUp, 85)
fillPrice = isUptrend ? shortStopPriceTrailPlotDisplay : longStopPriceTrailPlotDisplay

fill(plot(fillPrice, title='Williams Trailing Stop Fill High'), pricefill, color=fillColor)

alertCrossLongStop = na(longStopPriceTrailPlot) and not na(longStopPriceTrailPlot[1])
alertCrossShortStop = na(shortStopPriceTrailPlot) and not na(shortStopPriceTrailPlot[1])
alertcondition(alertCrossLongStop, title='Crossed Williams Long Stop', message='Alert from Williams Fractal Trailing Stops: \n {{ticker}} price crossed long stop')
alertcondition(alertCrossShortStop, title='Crossed Williams Short Stop', message='Alert from Williams Fractal Trailing Stops: \n {{ticker}} price crossed short stop')

alertcondition(isHighFractal, title='High Printed', message='Alert from Williams Fractal Trailing Stops: \n {{ticker}} Williams High has been confirmed')
alertcondition(isLowFractal, title='Low Printed', message='Alert from Williams Fractal Trailing Stops: \n {{ticker}} Williams Low has been confirmed')
import pandas as pd
import numpy as np
def find_fractals(df, left_range=2, right_range=2):
    high_fractals = []
    low_fractals = []

    for i in range(left_range, len(df) - right_range):
        is_high_fractal = all(df['High'][i] > df['High'][i - j] for j in range(1, left_range + 1)) and all(df['High'][i] > df['High'][i + j] for j in range(1, right_range + 1))
        is_low_fractal = all(df['Low'][i] < df['Low'][i - j] for j in range(1, left_range + 1)) and all(df['Low'][i] < df['Low'][i + j] for j in range(1, right_range + 1))

        high_fractals.append(df['High'][i] if is_high_fractal else np.nan)
        low_fractals.append(df['Low'][i] if is_low_fractal else np.nan)

    return pd.Series(high_fractals, index=df.index[left_range:len(df) - right_range]), pd.Series(low_fractals, index=df.index[left_range:len(df) - right_range])
def add_buffer(fractal_prices, buffer_percent, direction='minus'):
    if direction == 'plus':
        return fractal_prices * (1 + buffer_percent / 100)
    elif direction == 'minus':
        return fractal_prices * (1 - buffer_percent / 100)
    else:
        return fractal_prices
def update_trailing_stop(fractal_prices, previous_trail, price_data, direction='up'):
    trail = []
    for i in range(len(fractal_prices)):
        if direction == 'down' and price_data[i] >= previous_trail:
            trail.append(previous_trail)
        elif direction == 'up' and price_data[i] <= previous_trail:
            trail.append(previous_trail)
        else:
            trail.append(fractal_prices[i])
        previous_trail = trail[-1]
    return pd.Series(trail, index=fractal_prices.index)
def flip_trend(price_data, long_trail, short_trail):
    is_long = True
    long_trail_output = []
    short_trail_output = []

    for i in range(len(price_data)):
        if is_long and price_data[i] < short_trail[i]:
            is_long = False
        elif not is_long and price_data[i] > long_trail[i]:
            is_long = True

        long_trail_output.append(long_trail[i] if is_long else np.nan)
        short_trail_output.append(short_trail[i] if not is_long else np.nan)

    return pd.Series(long_trail_output, index=price_data.index), pd.Series(short_trail_output, index=price_data.index)
import matplotlib.pyplot as plt

def plot_trailing_stops(df, long_trail, short_trail):
    plt.figure(figsize=(14, 7))
    plt.plot(df.index, df['Close'], label='Price', color='black')
    plt.plot(long_trail.index, long_trail, label='Long Stop Price', linestyle='--', color='green')
    plt.plot(short_trail.index, short_trail, label='Short Stop Price', linestyle='--', color='red')
    plt.legend()
    plt.show()

# Assuming 'df' is your DataFrame containing the price data.
plot_trailing_stops(df, long_stop_price_trail, short_stop_price_trail)
import pandas as pd
import numpy as np
import matplotlib.pyplot as plt
import yfinance as yf
from datetime import datetime, timedelta

# Function to load data from Yahoo Finance
def load_stock_data(ticker, start_date, end_date):
    stock = yf.download(ticker, start=start_date, end=end_date)
    stock.reset_index(inplace=True)
    return stock

# Function to format the ticker for Yahoo Finance
def format_ticker(ticker):
    if ticker.isdigit():  # Korean stock ticker
        return ticker + ".KS"
    return ticker  # US or other tickers

# Load actual financial data
end_date = datetime.now()
start_date = end_date - timedelta(days=200)

ticker = 'AAPL'  # Example ticker, replace with desired ticker
ticker = format_ticker(ticker)

df = load_stock_data(ticker, start_date.strftime('%Y-%m-%d'), end_date.strftime('%Y-%m-%d'))

# 1. Find Fractals
def find_fractals(df, left_range=2, right_range=2):
    high_fractals = []
    low_fractals = []

    for i in range(left_range, len(df) - right_range):
        is_high_fractal = all(df['High'][i] > df['High'][i - j] for j in range(1, left_range + 1)) and all(df['High'][i] > df['High'][i + j] for j in range(1, right_range + 1))
        is_low_fractal = all(df['Low'][i] < df['Low'][i - j] for j in range(1, left_range + 1)) and all(df['Low'][i] < df['Low'][i + j] for j in range(1, right_range + 1))

        high_fractals.append(df['High'][i] if is_high_fractal else np.nan)
        low_fractals.append(df['Low'][i] if is_low_fractal else np.nan)

    high_fractals = [np.nan] * left_range + high_fractals + [np.nan] * right_range
    low_fractals = [np.nan] * left_range + low_fractals + [np.nan] * right_range

    return pd.Series(high_fractals, index=df.index), pd.Series(low_fractals, index=df.index)

df['High_Fractal'], df['Low_Fractal'] = find_fractals(df)

# 2. Add Buffer to Fractals
def add_buffer(fractal_prices, buffer_percent, direction='minus'):
    if direction == 'plus':
        return fractal_prices * (1 + buffer_percent / 100)
    elif direction == 'minus':
        return fractal_prices * (1 - buffer_percent / 100)
    else:
        return fractal_prices

df['High_Fractal_Buffered'] = add_buffer(df['High_Fractal'], buffer_percent=2, direction='plus')
df['Low_Fractal_Buffered'] = add_buffer(df['Low_Fractal'], buffer_percent=2, direction='minus')

# 3. Update Trailing Stop
def update_trailing_stop(fractal_prices, previous_trail, price_data, direction='up'):
    trail = []
    for i in range(len(fractal_prices)):
        if i == 0:
            trail.append(fractal_prices[i] if not np.isnan(fractal_prices[i]) else price_data[i])
        else:
            if direction == 'down' and price_data[i] >= previous_trail:
                trail.append(previous_trail)
            elif direction == 'up' and price_data[i] <= previous_trail:
                trail.append(previous_trail)
            else:
                trail.append(fractal_prices[i] if not np.isnan(fractal_prices[i]) else previous_trail)
        previous_trail = trail[-1]
    return pd.Series(trail, index=fractal_prices.index)

df['Long_Trailing_Stop'] = update_trailing_stop(df['Low_Fractal_Buffered'], df['Low_Fractal_Buffered'].iloc[0], df['Close'], direction='up')
df['Short_Trailing_Stop'] = update_trailing_stop(df['High_Fractal_Buffered'], df['High_Fractal_Buffered'].iloc[0], df['Close'], direction='down')

# 4. Flip Trend Logic
def flip_trend(price_data, long_trail, short_trail):
    is_long = True
    long_trail_output = []
    short_trail_output = []

    for i in range(len(price_data)):
        if is_long and price_data[i] < short_trail[i] and not np.isnan(short_trail[i]):
            is_long = False
        elif not is_long and price_data[i] > long_trail[i] and not np.isnan(long_trail[i]):
            is_long = True

        long_trail_output.append(long_trail[i] if is_long else np.nan)
        short_trail_output.append(short_trail[i] if not is_long else np.nan)

    return pd.Series(long_trail_output, index=price_data.index), pd.Series(short_trail_output, index=price_data.index)

df['Long_Trail_Output'], df['Short_Trail_Output'] = flip_trend(df['Close'], df['Long_Trailing_Stop'], df['Short_Trailing_Stop'])

# 5. Plotting the Results
def plot_trailing_stops(df):
    plt.figure(figsize=(14, 7))
    plt.plot(df.index, df['Close'], label='Price', color='black')
    plt.plot(df.index, df['Long_Trail_Output'], label='Long Stop Price', linestyle='--', color='green')
    plt.plot(df.index, df['Short_Trail_Output'], label='Short Stop Price', linestyle='--', color='red')
    plt.scatter(df.index, df['High_Fractal'], label='High Fractal', marker='v', color='blue')
    plt.scatter(df.index, df['Low_Fractal'], label='Low Fractal', marker='^', color='orange')
    plt.legend()
    plt.show()

plot_trailing_stops(df)
``` from flask import Flask, request, jsonify, render_template, redirect, url_for
import pandas as pd
import numpy as np
import yfinance as yf
from datetime import datetime, timedelta
import matplotlib
matplotlib.use('Agg')  # Use a non-GUI backend for Matplotlib
import matplotlib.pyplot as plt
import io
import base64
import os

app = Flask(__name__)

# Function to load data from Yahoo Finance
def load_stock_data(ticker, start_date, end_date):
    stock = yf.download(ticker, start=start_date, end=end_date)
    stock.reset_index(inplace=True)
    return stock

# Function to format the ticker for Yahoo Finance
def format_ticker(ticker):
    if ticker.isdigit():  # Korean stock ticker
        return ticker + ".KS"
    return ticker  # US or other tickers

# DMI and ADX Calculation
def calculate_dmi_adx(data, period=14):
    high = data['High']
    low = data['Low']
    close = data['Close']

    plus_dm = high.diff()
    minus_dm = low.diff()

    plus_dm[plus_dm < 0] = 0
    minus_dm[minus_dm > 0] = 0

    tr1 = high - low
    tr2 = abs(high - close.shift(1))
    tr3 = abs(low - close.shift(1))
    tr = pd.concat([tr1, tr2, tr3], axis=1).max(axis=1)

    tr_smooth = tr.rolling(window=period).sum()
    plus_dm_smooth = plus_dm.rolling(window=period).sum()
    minus_dm_smooth = abs(minus_dm.rolling(window=period).sum())

    plus_di = 100 * (plus_dm_smooth / tr_smooth)
    minus_di = 100 * (minus_dm_smooth / tr_smooth)

    dx = (abs(plus_di - minus_di) / (plus_di + minus_di)) * 100
    adx = dx.rolling(window=period).mean()

    return plus_di, minus_di, adx

@app.route('/')
def index():
    return render_template('index.html')

@app.route('/analyze', methods=['POST'])
def analyze():
    ticker = request.form['ticker']
    ticker = format_ticker(ticker)

    end_date = datetime.now()
    start_date = end_date - timedelta(days=200)

    data = load_stock_data(ticker, start_date.strftime('%Y-%m-%d'), end_date.strftime('%Y-%m-%d'))

    if data.empty:
        return render_template('index.html', error="No data found for the given ticker. Please try again.")

    # Apply DMI and ADX calculation
    data['plus_di'], data['minus_di'], data['adx'] = calculate_dmi_adx(data)

    # Calculate Bostian's IIX
    data['bostian_iix'] = (data['Volume'] * ((data['Close'] - data['Low']) ** 2 - (data['High'] - data['Close']) ** 2) / (data['High'] - data['Low'])).cumsum()

    # Volatility Analysis (Bollinger Bandwidth)
    length = 20
    source = data['Close']
    basis = source.rolling(window=length).mean()
    dev = source.rolling(window=length).std()
    upper = basis + 2 * dev
    lower = basis - 2 * dev
    data['bandwidth'] = (upper - lower) / basis * 100

    # Historical Mean and Standard Deviation
    data['historical_mean'] = data['bandwidth'].rolling(window=length).mean()
    data['historical_std'] = data['bandwidth'].rolling(window=length).std()

    # Percentile Calculation
    data['percentile'] = data['bandwidth'].rolling(window=length).apply(lambda x: np.sum(x <= x.iloc[-1]) / len(x) * 100, raw=False)

    # Regime Classification
    data['regime'] = np.where(data['bandwidth'] > (data['historical_mean'] + data['historical_std']), 'High',
                              np.where(data['bandwidth'] < (data['historical_mean'] - data['historical_std']), 'Low', 'Normal'))

    # Plot Results
    plt.figure(figsize=(14, 12))

    # Plot Closing Price
    plt.subplot(4, 1, 1)
    plt.plot(data['Date'], data['Close'], label='Close Price', color='black')
    plt.title('Stock Price')
    plt.legend()

    # Plot Bollinger Bandwidth
    plt.subplot(4, 1, 2)
    plt.plot(data['Date'], data['bandwidth'], label='Bandwidth', color='blue')
    plt.plot(data['Date'], data['historical_mean'], label='Mean', color='green')
    plt.plot(data['Date'], data['historical_mean'] + data['historical_std'], label='Mean + 1 Std Dev', color='red')
    plt.plot(data['Date'], data['historical_mean'] - data['historical_std'], label='Mean - 1 Std Dev', color='red')
    plt.title('Volatility Analysis (Bandwidth)')
    plt.legend()

    # Plot Bostian's IIX
    plt.subplot(4, 1, 3)
    plt.plot(data['Date'], data['bostian_iix'], label="Bostian's IIX", color='#2962FF')
    plt.fill_between(data['Date'], data['bostian_iix'], color=np.where(data['bostian_iix'] >= 0, '#26A69A', '#B2DFDB'))
    plt.title("Bostian's IIX Accumulated")
    plt.legend()

    # Plot Percentile of Bandwidth
    plt.subplot(4, 1, 4)
    plt.plot(data['Date'], data['percentile'], label='Bandwidth Percentile', color='purple')
    plt.axhline(50, color='gray', linestyle='--', label='Median (50th Percentile)')
    plt.title('Percentile of Bandwidth')
    plt.legend()

    plt.tight_layout()

    # Save plot to a PNG image and encode it to base64
    img = io.BytesIO()
    plt.savefig(img, format='png')
    img.seek(0)
    plot_url = base64.b64encode(img.getvalue()).decode()
    plt.close()

    return render_template('result.html', plot_url=plot_url)


@app.route('/favicon.ico')
def favicon():
    return '', 204

if __name__ == "__main__":
    port = int(os.environ.get("PORT", 5000))
    app.run(host="0.0.0.0", port=port, debug=False)  ```   이 코드를 보면 한국과 미국의 주식, etf, Index data를  yFinance에서 불러오는 backend code야.  우리가 만든 Python Trailing Stop Script 에 ' 한국과 미국의 주식, etf, Index data를  yFinance에서 불러오는 backend code  '로 수정해 줄 수 있어?
``` <!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Stock Analysis</title>
    <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0-alpha1/dist/css/bootstrap.min.css">
</head>
<body>
    <div class="container mt-5">
        <h1 class="text-center">Stock, ETF, and Index Analysis</h1>
        <form action="/analyze" method="POST" class="mt-4">
            <div class="mb-3">
                <label for="ticker" class="form-label">Enter Stock/ETF/Index Ticker</label>
                <input type="text" id="ticker" name="ticker" class="form-control" placeholder="e.g., 005930 for Samsung, AAPL for Apple" required>
            </div>
            <button type="submit" class="btn btn-primary">Analyze</button>
        </form>
        <div class="mt-5 text-center">
            <h3>Other Tools</h3>
            <div class="d-grid gap-3 d-md-flex justify-content-md-center">
                <a href="https://dmi-analysis-app-124-2011c455203e.herokuapp.com/" class="btn btn-secondary">DMI Analysis</a>
                <a href="https://stocksnalysis-49edb39e1e84.herokuapp.com/" class="btn btn-secondary">Price Analysis</a>
                <a href="https://bbhr-12e553aa0a18.herokuapp.com/" class="btn btn-secondary">Bollinger Bands Indicator</a>
            </div>
        </div>
    </div>
</body>
</html>  ``` 와  ``` <!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Analysis Result</title>
    <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0-alpha1/dist/css/bootstrap.min.css">
</head>
<body>
    <div class="container mt-5">
        <h1 class="text-center">Analysis Result</h1>
        <div class="text-center mt-4">
            <img src="data:image/png;base64,{{ plot_url }}" alt="Analysis Plot" class="img-fluid">
        </div>
        <div class="text-center mt-4">
            <a href="/" class="btn btn-primary">Analyze Another Ticker</a>
        </div>
    </div>
</body>
</html>  ``` 은 바로 전에 참조한  backend code의 front Index.html이야,  Python Trailing Stop Script ㅇ의 frontend  Index.html 도 잘 만들어줄 수있어?
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Trailing Stop Analysis</title>
    <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0-alpha1/dist/css/bootstrap.min.css">
</head>
<body>
    <div class="container mt-5">
        <h1 class="text-center">Trailing Stop Analysis for Stocks, ETFs, and Indices</h1>
        <form action="/analyze" method="POST" class="mt-4">
            <div class="mb-3">
                <label for="ticker" class="form-label">Enter Stock/ETF/Index Ticker</label>
                <input type="text" id="ticker" name="ticker" class="form-control" placeholder="e.g., 005930 for Samsung, AAPL for Apple" required>
            </div>
            <button type="submit" class="btn btn-primary">Analyze</button>
        </form>
        <div class="mt-5 text-center">
            <h3>Other Tools</h3>
            <div class="d-grid gap-3 d-md-flex justify-content-md-center">
                <a href="https://dmi-analysis-app-124-2011c455203e.herokuapp.com/" class="btn btn-secondary">DMI Analysis</a>
                <a href="https://stocksnalysis-49edb39e1e84.herokuapp.com/" class="btn btn-secondary">Price Analysis</a>
                <a href="https://bbhr-12e553aa0a18.herokuapp.com/" class="btn btn-secondary">Bollinger Bands Indicator</a>
            </div>
        </div>
    </div>
</body>
</html>

이 frontend 와 backend code를 web service를 위해 Heroku 에 deploy 할 수 있는 코드로 만들어줘

import pandas as pd
import numpy as np
import matplotlib.pyplot as plt
import yfinance as yf
from flask import Flask, request, render_template, redirect, url_for
from datetime import datetime, timedelta
import io
import base64
import os

# Use a non-GUI backend for Matplotlib
plt.switch_backend('Agg')

# Create Flask app
app = Flask(__name__)

# Function to load data from Yahoo Finance
def load_stock_data(ticker, start_date, end_date):
    stock = yf.download(ticker, start=start_date, end=end_date)
    stock.reset_index(inplace=True)
    return stock

# Function to format the ticker for Yahoo Finance
def format_ticker(ticker):
    if ticker.isdigit():  # Korean stock ticker
        return ticker + ".KS"
    return ticker  # US or other tickers

# Route for the index page
@app.route('/')
def index():
    return render_template('index.html')

# Route for analyzing the stock data
@app.route('/analyze', methods=['POST'])
def analyze():
    ticker = request.form['ticker']
    ticker = format_ticker(ticker)

    end_date = datetime.now()
    start_date = end_date - timedelta(days=200)

    df = load_stock_data(ticker, start_date.strftime('%Y-%m-%d'), end_date.strftime('%Y-%m-%d'))

    if df.empty:
        return render_template('index.html', error="No data found for the given ticker. Please try again.")

    # 1. Find Fractals
    def find_fractals(df, left_range=2, right_range=2):
        high_fractals = []
        low_fractals = []

        for i in range(left_range, len(df) - right_range):
            is_high_fractal = all(df['High'][i] > df['High'][i - j] for j in range(1, left_range + 1)) and all(df['High'][i] > df['High'][i + j] for j in range(1, right_range + 1))
            is_low_fractal = all(df['Low'][i] < df['Low'][i - j] for j in range(1, left_range + 1)) and all(df['Low'][i] < df['Low'][i + j] for j in range(1, right_range + 1))

            high_fractals.append(df['High'][i] if is_high_fractal else np.nan)
            low_fractals.append(df['Low'][i] if is_low_fractal else np.nan)

        high_fractals = [np.nan] * left_range + high_fractals + [np.nan] * right_range
        low_fractals = [np.nan] * left_range + low_fractals + [np.nan] * right_range

        return pd.Series(high_fractals, index=df.index), pd.Series(low_fractals, index=df.index)

    df['High_Fractal'], df['Low_Fractal'] = find_fractals(df)

    # 2. Add Buffer to Fractals
    def add_buffer(fractal_prices, buffer_percent, direction='minus'):
        if direction == 'plus':
            return fractal_prices * (1 + buffer_percent / 100)
        elif direction == 'minus':
            return fractal_prices * (1 - buffer_percent / 100)
        else:
            return fractal_prices

    df['High_Fractal_Buffered'] = add_buffer(df['High_Fractal'], buffer_percent=2, direction='plus')
    df['Low_Fractal_Buffered'] = add_buffer(df['Low_Fractal'], buffer_percent=2, direction='minus')

    # 3. Update Trailing Stop
    def update_trailing_stop(fractal_prices, previous_trail, price_data, direction='up'):
        trail = []
        for i in range(len(fractal_prices)):
            if i == 0:
                trail.append(fractal_prices[i] if not np.isnan(fractal_prices[i]) else price_data[i])
            else:
                if direction == 'down' and price_data[i] >= previous_trail:
                    trail.append(previous_trail)
                elif direction == 'up' and price_data[i] <= previous_trail:
                    trail.append(previous_trail)
                else:
                    trail.append(fractal_prices[i] if not np.isnan(fractal_prices[i]) else previous_trail)
            previous_trail = trail[-1]
        return pd.Series(trail, index=fractal_prices.index)

    df['Long_Trailing_Stop'] = update_trailing_stop(df['Low_Fractal_Buffered'], df['Low_Fractal_Buffered'].iloc[0], df['Close'], direction='up')
    df['Short_Trailing_Stop'] = update_trailing_stop(df['High_Fractal_Buffered'], df['High_Fractal_Buffered'].iloc[0], df['Close'], direction='down')

    # 4. Flip Trend Logic
    def flip_trend(price_data, long_trail, short_trail):
        is_long = True
        long_trail_output = []
        short_trail_output = []

        for i in range(len(price_data)):
            if is_long and price_data[i] < short_trail[i] and not np.isnan(short_trail[i]):
                is_long = False
            elif not is_long and price_data[i] > long_trail[i] and not np.isnan(long_trail[i]):
                is_long = True

            long_trail_output.append(long_trail[i] if is_long else np.nan)
            short_trail_output.append(short_trail[i] if not is_long else np.nan)

        return pd.Series(long_trail_output, index=price_data.index), pd.Series(short_trail_output, index=price_data.index)

    df['Long_Trail_Output'], df['Short_Trail_Output'] = flip_trend(df['Close'], df['Long_Trailing_Stop'], df['Short_Trailing_Stop'])

    # Plotting the Results
    plt.figure(figsize=(14, 7))
    plt.plot(df.index, df['Close'], label='Price', color='black')
    plt.plot(df.index, df['Long_Trail_Output'], label='Long Stop Price', linestyle='--', color='green')
    plt.plot(df.index, df['Short_Trail_Output'], label='Short Stop Price', linestyle='--', color='red')
    plt.scatter(df.index, df['High_Fractal'], label='High Fractal', marker='v', color='blue')
    plt.scatter(df.index, df['Low_Fractal'], label='Low Fractal', marker='^', color='orange')
    plt.legend()
    plt.tight_layout()

    # Save plot to a PNG image and encode it to base64
    img = io.BytesIO()
    plt.savefig(img, format='png')
    img.seek(0)
    plot_url = base64.b64encode(img.getvalue()).decode()
    plt.close()

    return render_template('result.html', plot_url=plot_url)

if __name__ == "__main__":
    port = int(os.environ.get("PORT", 5000))
    app.run(host="0.0.0.0", port=port, debug=False)

# Procfile
# web: python main.py

# requirements.txt
# Flask
# pandas
# numpy
# matplotlib
# yfinance
# gunicorn
``` c:\_Fractal Trail\app.py:115: UserWarning: Starting a Matplotlib GUI outside of the main thread will likely fail.
  plt.figure(figsize=(14, 7))
[2024-11-30 19:51:14,771] ERROR in app: Exception on /analyze [POST] ```   ``` jinja2.exceptions.TemplateNotFound: result.html
127.0.0.1 - - [30/Nov/2024 19:51:14] "POST /analyze HTTP/1.1" 500 - ```

result.html 파일의 코드를 작성해드리길 원합니당

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Analysis Result</title>
    <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0-alpha1/dist/css/bootstrap.min.css">
</head>
<body>
    <div class="container mt-5">
        <h1 class="text-center">Analysis Result</h1>
        <div class="text-center mt-4">
            <img src="data:image/png;base64,{{ plot_url }}" alt="Analysis Plot" class="img-fluid">
        </div>
        <div class="text-center mt-4">
            <a href="/" class="btn btn-primary">Analyze Another Ticker</a>
        </div>
    </div>
</body>
</html>

requirements.txt: 어떻게 구성할까?

Flask==2.0.3
Werkzeug==2.0.3
pandas==1.4.3
numpy==1.23.1
matplotlib==3.5.2
yfinance==0.1.70
gunicorn==20.1.0
Jinja2==3.1.2


``` remote:  !     Currently the root directory of your app contains:
remote:  !     
remote:  !     app.py
remote:  !     apppy_good.py
remote:  !     Procfile
remote:  !     Requirements.txt
remote:  !     runtime.txt
remote:  !     templates/ ```   Requirements.txt 을 requirements.txt 으로 수정해서 올리려는데 어떻게 해?

파일 이름 변경:

mv Requirements.txt requirements.txt

Git에 변경 사항 추가:

git add requirements.txt
git rm Requirements.txt

커밋:

git commit -m "Rename Requirements.txt to requirements.txt"

댓글 달기

이메일 주소는 공개되지 않습니다. 필수 필드는 *로 표시됩니다

위로 스크롤