Monday, September 8, 2025
No Result
View All Result
The Financial Observer
  • Home
  • Business
  • Economy
  • Stocks
  • Markets
  • Investing
  • Crypto
  • PF
  • Startups
  • Forex
  • Fintech
  • Real Estate
  • Analysis
  • Home
  • Business
  • Economy
  • Stocks
  • Markets
  • Investing
  • Crypto
  • PF
  • Startups
  • Forex
  • Fintech
  • Real Estate
  • Analysis
No Result
View All Result
The Financial Observer
No Result
View All Result
Home Cryptocurrency

A Simple Candle Pattern Strategy Delivered 65% Win Rate in Backtesting | by Ziad Francis, PhD | The Capital

A Simple Candle Pattern Strategy Delivered 65% Win Rate in Backtesting | by Ziad Francis, PhD | The Capital
Share on FacebookShare on Twitter


I backtested a candle sample printed by Michael Harris, displaying constructive outcomes

Press enter or click on to view picture in full dimension

Algorithmic buying and selling lovers are all the time in search of strong methods, and candle patterns are a timeless favourite. On this article, we are going to undergo a strong sample from Michael Harris’s guide, examined rigorously utilizing Python. This straightforward but efficient technique demonstrated a 65% win charge and a 71% revenue on main shares just like the S&P 500. With step-by-step coding steering and insights into the entry standards, this can be a must-read for anybody seeking to elevate their buying and selling sport utilizing automation.

The complete backtest outcomes will likely be introduced within the following fairness chart:

Press enter or click on to view picture in full dimension

1. Getting ready the Knowledge: Studying and Cleansing Candle Knowledge

import pandas as pdimport pandas_ta as tafrom tqdm import tqdmimport osimport numpy as npimport plotly.graph_objects as gofrom plotly.subplots import make_subplots

tqdm.pandas()

def read_csv_to_dataframe(file_path):df = pd.read_csv(file_path)df[“Gmt time”] = df[“Gmt time”].str.substitute(“.000”, “”)df[‘Gmt time’] = pd.to_datetime(df[‘Gmt time’], format=’%d.%m.%Y %H:%M:%S’)df = df[df.High != df.Low]df.set_index(“Gmt time”, inplace=True)return df

def read_data_folder(folder_path=”./information”):dataframes = []file_names = []for file_name in tqdm(os.listdir(folder_path)):if file_name.endswith(‘.csv’):file_path = os.path.be a part of(folder_path, file_name)df = read_csv_to_dataframe(file_path)dataframes.append(df)file_names.append(file_name)return dataframes, file_names

Step one in any backtesting undertaking is to organize the information, and this Python script ensures the information is clear and structured for evaluation. The code imports important libraries like pandas for information manipulation, pandas_ta for technical evaluation indicators, and plotly for visualization.

The read_csv_to_dataframe perform processes particular person CSV recordsdata, guaranteeing timestamps are correctly formatted and invalid rows (the place Excessive equals Low) are eliminated.The read_data_folder perform scans a folder of CSV recordsdata, processes them utilizing read_csv_to_dataframe, and returns an inventory of cleaned dataframes together with their filenames. This perform is used when we have to run the technique on a couple of asset for instance utilizing a number of information recordsdata.The usage of tqdm supplies a progress bar, making it simple to observe the processing of enormous datasets.

The information recordsdata I used and the total python code with a video walk-through can be found on YouTube when you want extra particulars:

2. Implementing the Candle Sample Logic

def total_signal(df, current_candle):current_pos = df.index.get_loc(current_candle)

c1 = df[‘High’].iloc[current_pos] > df[‘Close’].iloc[current_pos]c2 = df[‘Close’].iloc[current_pos] > df[‘High’].iloc[current_pos-2]c3 = df[‘High’].iloc[current_pos-2] > df[‘High’].iloc[current_pos-1]c4 = df[‘High’].iloc[current_pos-1] > df[‘Low’].iloc[current_pos]c5 = df[‘Low’].iloc[current_pos] > df[‘Low’].iloc[current_pos-2]c6 = df[‘Low’].iloc[current_pos-2] > df[‘Low’].iloc[current_pos-1]

if c1 and c2 and c3 and c4 and c5 and c6:return 2

# Add the symmetrical situations for brief (go quick) if neededc1 = df[‘Low’].iloc[current_pos] < df[‘Open’].iloc[current_pos]c2 = df[‘Open’].iloc[current_pos] < df[‘Low’].iloc[current_pos-2]c3 = df[‘Low’].iloc[current_pos-2] < df[‘Low’].iloc[current_pos-1]c4 = df[‘Low’].iloc[current_pos-1] < df[‘High’].iloc[current_pos]c5 = df[‘High’].iloc[current_pos] < df[‘High’].iloc[current_pos-2]c6 = df[‘High’].iloc[current_pos-2] < df[‘High’].iloc[current_pos-1]

if c1 and c2 and c3 and c4 and c5 and c6:return 1

return 0

This step defines the core of the technique by figuring out the precise candle sample that alerts entry factors. The perform total_signal evaluates whether or not the situations for a sample are met for a given candle.

Key elements of the sample logic:

Present Candle Place: Utilizing df.index.get_loc(current_candle), the perform identifies the place of the present candle within the DataFrame.

Situations for Lengthy Entry:

Situation 1: The excessive of the present candle is bigger than its closing value, indicating an higher wick.Situation 2: The closing value of the present candle is bigger than the excessive of the candle at place -2.Situation 3: The excessive of the candle at place -2 is bigger than the excessive of the candle at place -1.Situation 4: The excessive of the candle at place -1 is bigger than the low of the present candle.Situation 5: The low of the present candle is bigger than the low of the candle at place -2.Situation 6: The low of the candle at place -2 is bigger than the low of the candle at place -1.

Situations for Brief Entry:

Symmetrical to the lengthy entry logic, specializing in decrease wicks and downward momentum.

If all of the situations for an extended entry are happy, the perform returns 2. For a brief entry, it returns 1. If neither set of situations is met, it returns 0, signaling no commerce.

This logic interprets the visible sample into quantifiable guidelines, enabling its automated detection throughout backtesting. Subsequent, we’ll visualize the alerts on value chart and combine this logic right into a full buying and selling technique.

3. Visualizing Entry Factors on the Candlestick Chart

def add_total_signal(df):df[‘TotalSignal’] = df.progress_apply(lambda row: total_signal(df, row.title), axis=1)return df

def add_pointpos_column(df, signal_column):”””Provides a ‘pointpos’ column to the DataFrame to point the place of help and resistance factors.

Parameters:df (DataFrame): DataFrame containing the inventory information with the required SR column, ‘Low’, and ‘Excessive’ columns.sr_column (str): The title of the column to think about for the SR (help/resistance) factors.

Returns:DataFrame: The unique DataFrame with a further ‘pointpos’ column.”””def pointpos(row):if row[signal_column] == 2:return row[‘Low’] – 1e-4elif row[signal_column] == 1:return row[‘High’] + 1e-4else:return np.nan

df[‘pointpos’] = df.apply(lambda row: pointpos(row), axis=1)return df

def plot_candlestick_with_signals(df, start_index, num_rows):”””Plots a candlestick chart with sign factors.

Parameters:df (DataFrame): DataFrame containing the inventory information with ‘Open’, ‘Excessive’, ‘Low’, ‘Shut’, and ‘pointpos’ columns.start_index (int): The beginning index for the subset of information to plot.num_rows (int): The variety of rows of information to plot.

Returns:None”””df_subset = df[start_index:start_index + num_rows]

fig = make_subplots(rows=1, cols=1)

fig.add_trace(go.Candlestick(x=df_subset.index,open=df_subset[‘Open’],excessive=df_subset[‘High’],low=df_subset[‘Low’],shut=df_subset[‘Close’],title=’Candlesticks’),row=1, col=1)

fig.add_trace(go.Scatter(x=df_subset.index, y=df_subset[‘pointpos’], mode=”markers”,marker=dict(dimension=10, colour=”MediumPurple”, image=’circle’),title=”Entry Factors”),row=1, col=1)

fig.update_layout(width=1200, top=800, plot_bgcolor=’black’,paper_bgcolor=’black’,font=dict(colour=’white’),xaxis=dict(showgrid=False, zeroline=False),yaxis=dict(showgrid=False, zeroline=False),showlegend=True,legend=dict(x=0.01,y=0.99,traceorder=”regular”,font=dict(household=”sans-serif”,dimension=12,colour=”white”),bgcolor=”black”,bordercolor=”grey”,borderwidth=2))

fig.present()

After figuring out the candle patterns, the subsequent step is to map them to the dataset and visualize the outcomes. This part introduces capabilities to use the sample logic, mark entry factors, and plot the alerts on a candlestick chart.

Within the following picture we are able to see pattern of the information with the purple factors signaling a sample incidence, if the purpose is beneath the candle it alerts a bullish sample and in the wrong way if the purpose is above the candle it alerts a bearish route.

Press enter or click on to view picture in full dimension

4. Backtesting the Technique Throughout A number of Dataframes

from backtesting import Strategyfrom backtesting import Backtest

def SIGNAL():return df.TotalSignal

class MyStrat(Technique):mysize = 0.1 # Commerce sizeslperc = 0.04tpperc = 0.02

def init(self):tremendous().init()self.signal1 = self.I(SIGNAL) # Assuming SIGNAL is a perform that returns alerts

def subsequent(self):tremendous().subsequent()

if self.signal1 == 2 and never self.place:# Open a brand new lengthy place with calculated SL and TPcurrent_close = self.information.Shut[-1]sl = current_close – self.slperc * current_close # SL at 4% beneath the shut pricetp = current_close + self.tpperc * current_close # TP at 2% above the shut priceself.purchase(dimension=self.mysize, sl=sl, tp=tp)

elif self.signal1 == 1 and never self.place:# Open a brand new quick place, setting SL based mostly on a strategy-specific requirementcurrent_close = self.information.Shut[-1]sl = current_close + self.slperc * current_close # SL at 4% beneath the shut pricetp = current_close – self.tpperc * current_close # TP at 2% above the shut priceself.promote(dimension=self.mysize, sl=sl, tp=tp)

Backtesting Framework

Defining the Technique:The MyStrat class inherits from the Technique module within the backtesting library:Sign Integration: The SIGNAL perform provides the alerts generated earlier.Place Administration: A brand new lengthy place is opened when the sign is 2 (lengthy entry), with cease loss (SL) and take revenue (TP) ranges dynamically calculated based mostly on percentages of the closing value.

Loading a number of information recordsdata

folder_path = “./data_forex”dataframes, file_names = read_data_folder(folder_path)

for i, df in enumerate(dataframes):print(“engaged on dataframe “, i, “…”)df = add_total_signal(df)df = add_pointpos_column(df, “TotalSignal”)dataframes[i] = df # Replace the dataframe within the record

This code reads a folder of information recordsdata and hundreds the information into a number of information frames.

Backtest Execution

outcomes = []heatmaps = []

for df in dataframes:bt = Backtest(df, MyStrat, money=5000, margin=1/5, fee=0.0002)stats, heatmap = bt.optimize(slperc=[i/100 for i in range(1, 8)],tpperc=[i/100 for i in range(1, 8)],maximize=’Return [%]’, max_tries=3000,random_state=0,return_heatmap=True)outcomes.append(stats)heatmaps.append(heatmap)

Every dataframe is examined utilizing the Backtest module, initialized with $5,000 beginning money, a 20% margin, and a fee of 0.02%.Parameters like slperc (cease loss) and tpperc (take revenue) are optimized utilizing a grid search to maximise returns.

Aggregating Outcomes

agg_returns = sum([r[“Return [%]”] for r in outcomes])num_trades = sum([r[“# Trades”] for r in outcomes])max_drawdown = min([r[“Max. Drawdown [%]”] for r in outcomes])avg_drawdown = sum([r[“Avg. Drawdown [%]”] for r in outcomes]) / len(outcomes)

win_rate = sum([r[“Win Rate [%]”] for r in outcomes]) / len(outcomes)best_trade = max([r[“Best Trade [%]”] for r in outcomes])worst_trade = min([r[“Worst Trade [%]”] for r in outcomes])avg_trade = sum([r[“Avg. Trade [%]”] for r in outcomes]) / len(outcomes)

print(f”Aggregated Returns: {agg_returns:.2f}%”)print(f”Variety of Trades: {num_trades}”)print(f”Most Drawdown: {max_drawdown:.2f}%”)print(f”Common Drawdown: {avg_drawdown:.2f}%”)print(f”Win Charge: {win_rate:.2f}%”)print(f”Greatest Commerce: {best_trade:.2f}%”)print(f”Worst Commerce: {worst_trade:.2f}%”)print(f”Common Commerce: {avg_trade:.2f}%”)

Outcomes throughout all dataframes are aggregated to calculate key metrics:

Aggregated Returns: Whole proportion return throughout all datasets.Variety of Trades: Whole variety of trades executed.Most and Common Drawdown: The deepest and common dips within the account stability.Win Charge: Share of trades that ended profitably.Greatest and Worst Commerce: The very best and lowest returns from particular person trades.Common Commerce Efficiency: Common return per commerce.

Plotting The Fairness Curves

equity_curves = [stats[‘_equity_curve’][‘Equity’] for stats in outcomes]max_length = max(len(fairness) for fairness in equity_curves)

# Pad every fairness curve with the final worth to match the utmost lengthpadded_equity_curves = []for fairness in equity_curves:last_value = fairness.iloc[-1]padding = [last_value] * (max_length – len(fairness))padded_equity = fairness.tolist() + paddingpadded_equity_curves.append(padded_equity)

equity_df = pd.DataFrame(padded_equity_curves).T

import matplotlib.pyplot as plt

equity_df.plot(sort=’line’, figsize=(10, 6), legend=True).set_facecolor(‘black’)plt.gca().spines[‘bottom’].set_color(‘black’)plt.gca().spines[‘left’].set_color(‘black’)plt.gca().tick_params(axis=’x’, colours=’black’)plt.gca().tick_params(axis=’y’, colours=’black’)plt.gca().set_facecolor(‘black’)plt.legend(file_names)

Press enter or click on to view picture in full dimension

5. Conclusion

We will see that the sample reults are constructive on some belongings and never very promising on others. The difficulty right here is that I examined this technique on Foreign exchange information however Michael Harris described it in his guide for shares information, this could be affecting the outcomes as nicely. Nonetheless I strongly imagine that if we determine 5 patterns as this one and we run these concurrently on let’s say 10 completely different belongings, this could be a great starter for a buying and selling system, that may be simply automated not less than signaling potential trades and sending alerts to the human dealer. Clearly the system just isn’t absolutely automated as a result of a dealer nonetheless must confirm the validity of the sign, however the algorithm is doing the ready time and probing the market on behalf of the dealer… which is extra comfy than buying and selling in full guide mode.



Source link

Tags: backtestingCandleCapitalDeliveredFrancisPatternPhDRateSimplestrategyWinZiad
Previous Post

B-Stock’s Director of Strategic Operations, Joanie Stolos, Named Recipient of 2025 Women in Supply Chain Award

Next Post

Will Powell Hint At 100bps Cuts And Ignite A Bull Run? (NYSEARCA:SPY)

Related Posts

Bitcoin Price Weakens – Fresh Downside Risk If Bulls Fail Soon
Cryptocurrency

Bitcoin Price Weakens – Fresh Downside Risk If Bulls Fail Soon

September 8, 2025
Blockchain-Based Identity Can Help HR Navigate AI-Generated Applications
Cryptocurrency

Blockchain-Based Identity Can Help HR Navigate AI-Generated Applications

September 7, 2025
MARA’s Bitcoin Holdings Near B With 52,477 $BTC, Hyping Up Bitcoin Hyper
Cryptocurrency

MARA’s Bitcoin Holdings Near $6B With 52,477 $BTC, Hyping Up Bitcoin Hyper

September 6, 2025
Robinhood Soars 6% After S&P 500 Inclusion, Strategy Snubbed
Cryptocurrency

Robinhood Soars 6% After S&P 500 Inclusion, Strategy Snubbed

September 7, 2025
Senate Banking Committee Releases Updated Draft Crypto Market Structure Bill
Cryptocurrency

Senate Banking Committee Releases Updated Draft Crypto Market Structure Bill

September 6, 2025
Strategy confirms Bitcoin purchases are unaffected by new Nasdaq rules
Cryptocurrency

Strategy confirms Bitcoin purchases are unaffected by new Nasdaq rules

September 6, 2025
Next Post
Will Powell Hint At 100bps Cuts And Ignite A Bull Run? (NYSEARCA:SPY)

Will Powell Hint At 100bps Cuts And Ignite A Bull Run? (NYSEARCA:SPY)

QuickFee sells US Pay Now business to Aiwyn for A million

QuickFee sells US Pay Now business to Aiwyn for A$40 million

Leave a Reply Cancel reply

Your email address will not be published. Required fields are marked *

  • Trending
  • Comments
  • Latest
New Executive Order Will Allow Real Estate to Be Bracketed Into 401(k)s, Marking a Potential Investment Strategy Game Changer

New Executive Order Will Allow Real Estate to Be Bracketed Into 401(k)s, Marking a Potential Investment Strategy Game Changer

August 14, 2025
The Stock Market Just Did Something for the 16th Time Since 1950. It Usually Signals a Big Move in the Next Year.

The Stock Market Just Did Something for the 16th Time Since 1950. It Usually Signals a Big Move in the Next Year.

August 19, 2025
In praise of complicated investing strategies

In praise of complicated investing strategies

August 19, 2025
SEC and Ripple officially settle appeals, XRP case moves to final enforcement

SEC and Ripple officially settle appeals, XRP case moves to final enforcement

August 22, 2025
QuickFee sells US Pay Now business to Aiwyn for A million

QuickFee sells US Pay Now business to Aiwyn for A$40 million

September 8, 2025
Will Powell Hint At 100bps Cuts And Ignite A Bull Run? (NYSEARCA:SPY)

Will Powell Hint At 100bps Cuts And Ignite A Bull Run? (NYSEARCA:SPY)

September 8, 2025
A Simple Candle Pattern Strategy Delivered 65% Win Rate in Backtesting | by Ziad Francis, PhD | The Capital

A Simple Candle Pattern Strategy Delivered 65% Win Rate in Backtesting | by Ziad Francis, PhD | The Capital

September 8, 2025
B-Stock’s Director of Strategic Operations, Joanie Stolos, Named Recipient of 2025 Women in Supply Chain Award

B-Stock’s Director of Strategic Operations, Joanie Stolos, Named Recipient of 2025 Women in Supply Chain Award

September 8, 2025
Yen Drops Sharply Following Ishiba’s Resignation. Forecast as of 08.09.2025

Yen Drops Sharply Following Ishiba’s Resignation. Forecast as of 08.09.2025

September 8, 2025
Spacious Whispering Trails Retreat with Pool, Spa, and Half-Acre Lot in Jupiter

Spacious Whispering Trails Retreat with Pool, Spa, and Half-Acre Lot in Jupiter

September 8, 2025
The Financial Observer

Get the latest financial news, expert analysis, and in-depth reports from The Financial Observer. Stay ahead in the world of finance with up-to-date trends, market insights, and more.

Categories

  • Business
  • Cryptocurrency
  • Economy
  • Fintech
  • Forex
  • Investing
  • Market Analysis
  • Markets
  • Personal Finance
  • Real Estate
  • Startups
  • Stock Market
  • Uncategorized

Latest Posts

  • QuickFee sells US Pay Now business to Aiwyn for A$40 million
  • Will Powell Hint At 100bps Cuts And Ignite A Bull Run? (NYSEARCA:SPY)
  • A Simple Candle Pattern Strategy Delivered 65% Win Rate in Backtesting | by Ziad Francis, PhD | The Capital
  • About Us
  • Advertise with Us
  • Disclaimer
  • Privacy Policy
  • DMCA
  • Cookie Privacy Policy
  • Terms and Conditions
  • Contact us

Copyright © 2025 The Financial Observer.
The Financial Observer is not responsible for the content of external sites.

No Result
View All Result
  • Home
  • Business
  • Economy
  • Stocks
  • Markets
  • Investing
  • Crypto
  • PF
  • Startups
  • Forex
  • Fintech
  • Real Estate
  • Analysis

Copyright © 2025 The Financial Observer.
The Financial Observer is not responsible for the content of external sites.