Visualizing Recent Price-Volume Concentration Using 4D Charts
Monitoring volume and price flows can offer illuminating insights into directional momentum, but this approach does have its limitations.
For IWM, the following chart is the result of using yf to generate a volumetric 3D chart that illustrates volume flows across time and price range:
Recent volume activity has been observed in the 200–220 range. A month ago, this activity was observed in the 230-235 range, indicating a slight pullback into the new year. It will be curious to observe the direction of this flow in the coming week—and whether the down trend will continue or if more bullish headwinds will emerge. The caveat of relying on volume data is that it may often be delayed because the directional momentum is often not immediately clear.
Below is the 4D chart that animates the volume flow looking back a month and going forward in time, as well as the script used to generate it:
import yfinance as yf
import plotly.graph_objects as go
import imageio
import os
def get_stock_data(ticker):
"""Retrieve historical stock data for the entered ticker."""
stock = yf.Ticker(ticker)
data = stock.history(period="1mo", interval="1h") # 1-month hourly data
return data
def create_animated_gif(data):
"""Create a simplified animated 3D chart and save it as a GIF."""
prices = data['Close'].reset_index(drop=True) # Reset index for safe positional access
volumes = data['Volume'].reset_index(drop=True) # Reset index for safe positional access
time_stamps = data.index
# Normalize for visualization
sphere_sizes = (prices * volumes) / (prices.max() * volumes.max())
# Verify working directory and write access
working_dir = os.getcwd()
print(f"Current working directory: {working_dir}")
frames_path = os.path.abspath("frames")
os.makedirs(frames_path, exist_ok=True)
print(f"Frames will be saved to: {frames_path}")
# Generate frames and save them
for i in range(1, len(prices) + 1):
fig = go.Figure()
fig.add_trace(go.Scatter3d(
x=time_stamps[:i],
y=prices.iloc[:i],
z=volumes.iloc[:i],
mode='markers',
marker=dict(
size=sphere_sizes.iloc[:i] * 50,
color=prices.iloc[:i],
colorscale='Viridis',
opacity=0.8
)
))
frame_path = f"{frames_path}/frame_{i:03d}.png"
try:
# Use `orca` for exporting the image
fig.write_image(frame_path, engine="orca")
print(f"Saved frame: {frame_path}")
except Exception as e:
print(f"Error saving frame {frame_path}: {e}")
# Create GIF
gif_path = os.path.abspath("animated_chart.gif")
print(f"Creating GIF at: {gif_path}")
with imageio.get_writer(gif_path, mode="I", duration=0.1) as writer:
for i in range(1, len(prices) + 1):
frame_path = f"{frames_path}/frame_{i:03d}.png"
if os.path.exists(frame_path):
writer.append_data(imageio.imread(frame_path))
else:
print(f"Frame not found: {frame_path}")
print(f"GIF successfully created: {gif_path}")
def main():
ticker = input("Enter a stock ticker: ").strip()
data = get_stock_data(ticker)
create_animated_gif(data)
if __name__ == "__main__":
main()
Besides volume flow, with the right data set, the goal is to visualize second-order derivative functions, like chain greeks. More on this to come in future posts. Note that the script above is for generating a simple GIF file; more lines can be added to generate more complex HTML charts with interactive controls and even dropdown menus—a repository for this is in the works.
Here is SPY’s chart:
Here is QQQ’s chart:
NVDA:
TSLA:
MSFT:
F’s flow has been consolidating toward its monthly highs, making it a high-interest ticker: