One candlestick tells you what happened during one time interval. But markets do not stop after one candle.
The next useful question is: how is the current candle positioned compared with the candle before it?
We still will not add an indicator. Instead, we will compare only two things: High and Low.
current High vs previous High
current Low vs previous Low
From those two comparisons, we can begin describing whether price is moving upward, downward, or in a mixed way.
1. Do Not Start with an Indicator
A moving average, MACD, RSI, or another indicator can be useful later. But every indicator is calculated from price data.
So first, learn to see the structure in price itself.
price first
→ structure second
→ indicators later
In this lesson, the price structure comes directly from candle Highs and Lows.
2. Use Recent AAPL Data
As in the previous lesson, the date range is created when the script runs.
from datetime import date, timedelta
today = date.today()
start_date = (
today - timedelta(days=60)
).strftime("%Y-%m-%d")
end_date = today.strftime("%Y-%m-%d")
Then we keep the most recent 20 trading days:
recent_trading_days = 20
df = df.tail(
recent_trading_days
)
The exact dates will change over time. That is intentional.
You are practicing on recent market data rather than reproducing an old picture forever.
3. Price Structure Needs at Least Two Candles
One candle can tell you its own Open, High, Low, and Close. But it cannot be called a higher high or lower low by itself.
For that, you need a comparison.
previous candle
↓
compare
↓
current candle
That is why our loop starts from row 1 instead of row 0:
for i in range(1, len(df)):
Row 0 has no earlier row inside our selected data to compare with.
4. Select the Current and Previous Candles
Inside the loop:
current = df.iloc[i]
previous = df.iloc[i - 1]
Read this literally.
i
→ current row
i - 1
→ one row before it
If i = 5:
current = df.iloc[5]
previous = df.iloc[4]
Python can now compare the same columns from two neighboring trading days.
5. What Is a Higher High?
Read the two High prices:
current_high = float(
current["High"]
)
previous_high = float(
previous["High"]
)
Then compare them:
current_high > previous_high
If this is true, the current candle reached a higher price than the previous candle did.
current High
↑
previous High
We can describe that as a Higher High.
6. What Is a Higher Low?
Now compare the Lows:
current_low > previous_low
If true, the lowest price of the current candle is also above the lowest price of the previous candle.
current Low
↑
previous Low
We call that a Higher Low.
7. A Simple Rising Structure
Suppose both conditions are true:
current High > previous High
and
current Low > previous Low
Both the top and bottom of the candle's price range moved upward.
In this beginner lesson, we will call that:
Rising structure
The code is:
if (
current_high > previous_high
and current_low > previous_low
):
structure = "Rising structure"
This is a description of two neighboring candles. It is not yet a complete definition of an uptrend.
8. A Simple Falling Structure
Reverse the comparison:
current High < previous High
and
current Low < previous Low
Now both the upper and lower boundaries moved downward.
Falling structure
In Python:
elif (
current_high < previous_high
and current_low < previous_low
):
structure = "Falling structure"
Again, this describes one local comparison. A larger trend requires more context.
9. What If High and Low Disagree?
Markets often do not fit a clean two-word description.
For example:
Higher High
+
Lower Low
The current candle expanded beyond both sides of the previous candle.
Or:
Lower High
+
Higher Low
The current range contracted inside the previous range.
For now, we group these cases as:
Mixed structure
This is useful because it prevents us from forcing every candle into "up" or "down."
10. Print the Comparisons Before Looking at the Chart
The script prints a line for each comparison:
date
| Higher High or Lower/Equal High
| Higher Low or Lower/Equal Low
| Rising / Falling / Mixed structure
This follows the same Alphesta habit from the previous lesson:
numbers
→ comparison
→ chart
→ interpretation
11. Draw the Candles from Scratch Again
We keep using the wick-and-body logic from the earlier lessons.
ax.vlines(...)
→ wick
ax.bar(...)
→ body
That repetition is intentional. The code should become familiar rather than magical.
12. Add a High Line and a Low Line
To make the structure easier to see, collect all recent High values:
high_values = [
float(value)
for value in df["High"]
]
And the Low values:
low_values = [
float(value)
for value in df["Low"]
]
Then draw them:
ax.plot(
x_positions,
high_values,
marker="o",
label="High",
)
ax.plot(
x_positions,
low_values,
marker="o",
label="Low",
)
These lines do not create a technical indicator. They simply connect values that already exist in the OHLC table.
They help your eye answer:
Are recent Highs moving up?
Are recent Lows moving up?
Are both moving down?
Or are they disagreeing?
13. Keep the Chart Readable
This chart contains more information than the one-candle figure, so we use a wider figure and larger text.
figure_width = 14
figure_height = 8
title_fontsize = 22
label_fontsize = 15
tick_fontsize = 13
annotation_fontsize = 11
The figure becomes easier to read when many dates are shown.
We also show only every second date label:
date_tick_step = 2
If the dates still feel crowded, try:
date_tick_step = 3
14. Complete Code
from pathlib import Path
from datetime import date, timedelta
import os
import FinanceDataReader as fdr
import matplotlib.pyplot as plt
SCRIPT_DIR = Path(__file__).resolve().parent
os.chdir(SCRIPT_DIR)
symbol = "AAPL"
today = date.today()
start_date = (
today - timedelta(days=60)
).strftime("%Y-%m-%d")
end_date = today.strftime("%Y-%m-%d")
recent_trading_days = 20
candle_width = 0.6
figure_width = 14
figure_height = 8
title_fontsize = 22
label_fontsize = 15
tick_fontsize = 13
annotation_fontsize = 11
date_tick_step = 2
df = fdr.DataReader(
symbol,
start_date,
end_date,
)
df = df.tail(
recent_trading_days
)
if len(df) < 2:
raise ValueError(
"At least two trading days are needed "
"to compare price structure."
)
structure_rows = []
for i in range(1, len(df)):
current = df.iloc[i]
previous = df.iloc[i - 1]
current_date = (
df.index[i]
.strftime("%Y-%m-%d")
)
current_high = float(
current["High"]
)
current_low = float(
current["Low"]
)
previous_high = float(
previous["High"]
)
previous_low = float(
previous["Low"]
)
high_relation = (
"Higher High"
if current_high > previous_high
else "Lower/Equal High"
)
low_relation = (
"Higher Low"
if current_low > previous_low
else "Lower/Equal Low"
)
if (
current_high > previous_high
and current_low > previous_low
):
structure = "Rising structure"
elif (
current_high < previous_high
and current_low < previous_low
):
structure = "Falling structure"
else:
structure = "Mixed structure"
structure_rows.append(
(
current_date,
high_relation,
low_relation,
structure,
)
)
for row in structure_rows:
print(
f"{row[0]} | "
f"{row[1]} | "
f"{row[2]} | "
f"{row[3]}"
)
fig, ax = plt.subplots(
figsize=(
figure_width,
figure_height,
)
)
for i, (date_index, row) in enumerate(
df.iterrows()
):
open_price = float(
row["Open"]
)
high_price = float(
row["High"]
)
low_price = float(
row["Low"]
)
close_price = float(
row["Close"]
)
is_up_candle = (
close_price >= open_price
)
candle_color = (
"green"
if is_up_candle
else "red"
)
body_bottom = min(
open_price,
close_price,
)
body_height = abs(
close_price - open_price
)
visible_body_height = (
body_height
if body_height > 0
else max(
(high_price - low_price) * 0.01,
0.01,
)
)
ax.vlines(
x=i,
ymin=low_price,
ymax=high_price,
color=candle_color,
linewidth=1.6,
)
ax.bar(
x=i,
height=visible_body_height,
bottom=body_bottom,
width=candle_width,
color=candle_color,
edgecolor=candle_color,
)
high_values = [
float(value)
for value in df["High"]
]
low_values = [
float(value)
for value in df["Low"]
]
x_positions = list(
range(len(df))
)
ax.plot(
x_positions,
high_values,
marker="o",
linewidth=1.2,
label="High",
)
ax.plot(
x_positions,
low_values,
marker="o",
linewidth=1.2,
label="Low",
)
latest_current = df.iloc[-1]
latest_previous = df.iloc[-2]
latest_high = float(
latest_current["High"]
)
latest_low = float(
latest_current["Low"]
)
previous_high = float(
latest_previous["High"]
)
previous_low = float(
latest_previous["Low"]
)
if (
latest_high > previous_high
and latest_low > previous_low
):
latest_structure = (
"Rising structure"
)
elif (
latest_high < previous_high
and latest_low < previous_low
):
latest_structure = (
"Falling structure"
)
else:
latest_structure = (
"Mixed structure"
)
ax.text(
0.02,
0.97,
(
"Latest comparison: "
f"{latest_structure}"
),
transform=ax.transAxes,
va="top",
fontsize=annotation_fontsize,
bbox=dict(
boxstyle="round,pad=0.5",
alpha=0.08,
),
)
tick_positions = list(
range(
0,
len(df),
date_tick_step,
)
)
tick_labels = [
df.index[i].strftime(
"%Y-%m-%d"
)
for i in tick_positions
]
ax.set_xticks(
tick_positions
)
ax.set_xticklabels(
tick_labels,
rotation=45,
ha="right",
)
ax.set_title(
(
f"{symbol} — "
"Reading Rising and Falling Price"
),
fontsize=title_fontsize,
)
ax.set_ylabel(
"Price",
fontsize=label_fontsize,
)
ax.tick_params(
axis="both",
labelsize=tick_fontsize,
)
ax.legend(
fontsize=annotation_fontsize,
)
ax.grid(
axis="y",
alpha=0.20,
)
ax.set_xlim(
-1,
len(df),
)
plt.tight_layout()
output_file = (
SCRIPT_DIR
/ "rising_falling_price_with_candles.png"
)
plt.savefig(
output_file,
dpi=160,
bbox_inches="tight",
)
print()
print("Chart saved:")
print(output_file)
plt.show()
15. Change the Window Yourself
Find:
recent_trading_days = 20
Change it to:
recent_trading_days = 10
Run the script again.
Ask yourself:
Does the recent structure
look clearer or less clear?
Do the Highs and Lows
still appear to move together?
Did your interpretation change
because you changed the viewing window?
This is an important lesson: the same market can look different depending on how much history you display.
What You Just Learned
Higher High + Higher Low
→ simple rising structure
Lower High + Lower Low
→ simple falling structure
High and Low disagree
→ mixed structure
These are basic price relationships, not trading signals.
You also saw that we can describe price movement directly from OHLC data before calculating a single technical indicator.
Where Do We Go Next?
Now you know how to read one candle and how to compare neighboring candles.
The next step is to examine a few famous candlestick shapes, but we will treat them as testable descriptions of price behavior, not as guaranteed predictions.
We will begin with the hammer.