惯性聚合 高效追踪和阅读你感兴趣的博客、新闻、科技资讯
阅读原文 在惯性聚合中打开

推荐订阅源

有赞技术团队
有赞技术团队
Apple Machine Learning Research
Apple Machine Learning Research
IT之家
IT之家
奇客Solidot–传递最新科技情报
奇客Solidot–传递最新科技情报
B
Blog RSS Feed
酷 壳 – CoolShell
酷 壳 – CoolShell
人人都是产品经理
人人都是产品经理
Hugging Face - Blog
Hugging Face - Blog
博客园_首页
V
V2EX
aimingoo的专栏
aimingoo的专栏
爱范儿
爱范儿
博客园 - 聂微东
Cyber Security Advisories - MS-ISAC
Cyber Security Advisories - MS-ISAC
钛媒体:引领未来商业与生活新知
钛媒体:引领未来商业与生活新知
H
Hackread – Cybersecurity News, Data Breaches, AI and More
Stack Overflow Blog
Stack Overflow Blog
罗磊的独立博客
freeCodeCamp Programming Tutorials: Python, JavaScript, Git & More
MongoDB | Blog
MongoDB | Blog
Jina AI
Jina AI
T
The Blog of Author Tim Ferriss
月光博客
月光博客
云风的 BLOG
云风的 BLOG

DEV Community

Authentication Security Deep Dive: From Brute Force to Salted Hashing (With Java Examples) Why AI Systems Don’t Fail — They Drift Spilling beans for how i learn for exam😁"Reinforcement Learning Cheat Sheet" I Replaced Chrome with Safari for AI Browser Automation. Here's What Broke (and What Finally Worked) How Python Borrows Other People's Work The $40 Architecture: Processing 1 Billion API Requests with 99.99% Uptime Vibe Coding: A Workflow Guide (From Zero to SaaS) Most webhook security guides protect the wrong side. The scary part is delivery. Headless CMS for TanStack Start: Build a Blog with Cosmic EU Age Verification App "Hacked in 2 Minutes" — What Actually Happened Comfy Cloud’s delete function does not actually remove files Running AI Models on GPU Cloud Servers: A Beginner Guide Event-driven media intelligence with AWS Step Functions and Bedrock I scored 500 AI prompts across 8 quality dimensions — here's what broke How to Call Google Gemini API from Next.js (Free Tier, No Backend Needed) The Portal Protocol: Reclaiming Human Connection in the Age of AI How to Fix Your Team's Scattered Knowledge Problem With a Self-Hosted Forum Intro to tc Cloud Functors: A Graph-First Mental Model for the Modern Cloud Designing Multi-Tenant Backends With Both Ownership and Team Access I Built a Neumorphic CSS Library with 77+ Components — Here's What I Learned PostgreSQL Performance Optimization: Why Connection Pooling Is Critical at Scale Cómo construí un SaaS multi-rubro para gestionar expensas en Argentina con FastAPI + Vue 3 🚀 I Built an Ethical Hacking Scanner Tool – Open Source Project I Replaced /usage and /context in Claude Code With a Single Statusline A Pythonic Way to Handle Emails (IMAP/SMTP) with Auto-Discovery and AI-Ready Design I Collected 8.9 Million Polymarket Price Points — Here's What I Found About How Markets Really Move EcoTrack AI — Carbon Footprint Tracker & Dashboard Everyone's Using AI. No One Agrees How. 5 self-hosted ebook managers worth trying in 2026 Building Your First AI Agent with LangChain: From Chatbot to Autonomous Assistant
📈 The Jedi’s Guide to Building a Python Stock‑Market Trad...
Timevolt · 2026-06-19 · via DEV Community

Timevolt

(or: How I Turned My Laptop Into a Lightsaber for the Market)

The Quest Begins (The “Why”)

Ever stared at a blinking cursor at 2 a.m., wishing you could make your laptop do the heavy lifting while you chased dreams (or just caught up on sleep)? I was there, scrolling through Reddit’s r/investing, watching folks brag about “algo‑trading gains” while I was still manually refreshing Yahoo Finance like a peasant in a medieval market.

One night, after yet another failed attempt to predict a stock’s move with gut feeling (spoiler: my gut is terrible at math), I remembered a line from The Matrix: “There is no spoon.” Turns out, there is no magic either—just code, data, and a healthy dose of stubbornness. I decided to slay the dragon of emotion‑driven trading and build a bot that could execute a simple strategy while I binge‑watched Stranger Things.

Spoiler alert: the first version was a hot mess, but the journey taught me more about Python, APIs, and risk management than any textbook ever could. Let’s walk through that adventure together—code, pitfalls, and all the triumphant “I‑did‑it!” moments.

The Revelation (The Insight)

The big “aha!” moment came when I realized a trading bot isn’t some omniscient AI that predicts the future; it’s just a disciplined executor of rules you define. Think of it as Indiana Jones whip‑cracking through a booby‑trapped temple: you set the traps (your strategy), the bot avoids them (risk checks), and grabs the idol (profit) when the conditions are right.

For my first bot I chose a mean‑reversion idea: if a stock’s price deviates too far from its 20‑day moving average, I bet it’ll snap back. It’s not flashy, but it’s easy to understand, back‑test, and implement. The magic happens in three simple steps:

  1. Fetch data – pull recent price bars from a free API (I used Alpha Vantage; you can swap for Polygon, IEX Cloud, etc.).
  2. Calculate the signal – compare the latest close to the moving average and compute a z‑score.
  3. Execute – if the z‑score crosses a threshold, send a market order via a brokerage’s API (I used Alpaca for paper trading).

That’s it. No neural nets, no secret sauce—just a loop that checks the condition every minute and acts.

Wielding the Power (Code & Examples)

The Struggle: Hard‑coded Values & No Error Handling

My first attempt looked like this (don’t use this in production—seriously, it’s a trap!):

# WARNING: This is the "before" code – a classic rookie trap!
import requests, time

API_KEY = "YOUR_ALPHA_VANTAGE_KEY"
SYMBOL = "AAPL"
AVG_WINDOW = 20
THRESHOLD = 1.5   # z‑score trigger

def get_price():
    url = f"https://www.alphavantage.co/query?function=TIME_SERIES_INTRADAY&symbol={SYMBOL}&interval=1min&apikey={API_KEY}"
    data = requests.get(url).json()
    # Assume the latest minute is the first key in the nested dict
    latest = list(data["Time Series (1min)"].values())[0]
    return float(latest["4. close"])

def simple_moving_average(prices, window):
    return sum(prices[-window:]) / window

def z_score(price, avg, std):
    return (price - avg) / std if std != 0 else 0

prices = []
while True:
    price = get_price()
    prices.append(price)
    if len(prices) >= AVG_WINDOW:
        avg = simple_moving_average(prices, AVG_WINDOW)
        # naive std dev
        variance = sum((p - avg) ** 2 for p in prices[-AVG_WINDOW:]) / AVG_WINDOW
        std = variance ** 0.5
        z = z_score(price, avg, std)
        if z > THRESHOLD:
            print(f"SELL signal! Z={z:.2f}")
        elif z < -THRESHOLD:
            print(f"BUY signal! Z={z:.2f}")
    time.sleep(60)   # wait a minute

What went wrong?

  • The script assumes the JSON structure never changes – a single API format tweak and it crashes.
  • No exception handling: network hiccups send the whole loop into oblivion.
  • Standard deviation calculated on a tiny window is noisy; we got false signals left and right.
  • No order execution – just prints.

I felt like Neo trying to dodge bullets in The Matrix but getting hit by every stray bug.

The Victory: Robust, Modular, and Ready for Paper Trading

After a few hours of debugging (and a healthy dose of caffeine), I refactored the bot into bite‑sized pieces, added proper error handling, and hooked it up to Alpaca’s paper‑trading API. Here’s the “after” version—still simple, but now it won’t explode at the first hiccup.

# -------------------------------------------------
#  Simple Mean‑Reversion Bot – Production‑ish Version
# -------------------------------------------------
import os, time, logging, requests
import pandas as pd
import alpaca_trade_api as tradeapi

# ------------------- CONFIG -------------------
API_KEY_ALPHA = os.getenv("ALPHA_KEY")          # Alpha Vantage
API_KEY_ALPACA = os.getenv("APCA_API_KEY_ID")
API_SECRET_ALPACA = os.getenv("APCA_API_SECRET_KEY")
BASE_URL = "https://paper-api.alpaca.markets"   # switch to live when ready
SYMBOL = "AAPL"
AVG_WINDOW = 20
Z_THRESHOLD = 1.5
QUANTITY = 1                                    # shares per trade
SLEEP_SECONDS = 60
# ------------------------------------------------

logging.basicConfig(level=logging.INFO,
                    format="%(asctime)s %(levelname)s %(message)s")

# ----- Alpha Vantage Helper -----
def fetch_recent_bars(limit=100):
    """Return a DataFrame with the last `limit` minute bars for SYMBOL."""
    url = ("https://www.alphavantage.co/query"
           f"?function=TIME_SERIES_INTRADAY&symbol={SYMBOL}"
           f"&interval=1min&outputsize=compact&apikey={API_KEY_ALPHA}")
    try:
        resp = requests.get(url, timeout=10)
        resp.raise_for_status()
        data = resp.json()
        # The key we need changes based on interval; for 1min it's:
        ts_key = "Time Series (1min)"
        if ts_key not in data:
            logging.error("Unexpected API response: %s", data)
            return pd.DataFrame()
        df = pd.DataFrame.from_dict(data[ts_key], orient="index")
        df = df.rename(columns={
            "1. open": "open",
            "2. high": "high",
            "3. low": "low",
            "4. close": "close",
            "5. volume": "volume"
        })
        df = df.astype(float)
        df.index = pd.to_datetime(df.index)
        return df.sort_index().tail(limit)
    except Exception as e:
        logging.exception("Error fetching data: %s", e)
        return pd.DataFrame()

# ----- Alpaca Wrapper -----
alpaca = tradeapi.REST(API_KEY_ALPACA, API_SECRET_ALPACA, BASE_URL, api_version='v2')

def place_order(side):
    """Submit a market order via Alpaca."""
    try:
        alpaca.submit_order(
            symbol=SYMBOL,
            qty=QUANTITY,
            side=side,
            type='market',
            time_in_force='day'
        )
        logging.info("Placed %s order for %s shares of %s", side.upper(), QUANTITY, SYMBOL)
    except Exception as e:
        logging.exception("Order failed: %s", e)

# ----- Strategy Core -----
def evaluate_signal(df):
    """Return 'buy', 'sell', or None based on z‑score of close vs SMA."""
    if len(df) < AVG_WINDOW:
        return None
    close = df['close']
    sma = close.rolling(window=AVG_WINDOW).mean().iloc[-1]
    std = close.rolling(window=AVG_WINDOW).std().iloc[-1]
    if std == 0 or pd.isna(std):
        return None
    z = (close.iloc[-1] - sma) / std
    logging.debug("Latest close: %.2f, SMA: %.2f, STD: %.2f, Z: %.2f",
                  close.iloc[-1], sma, std, z)
    if z > Z_THRESHOLD:
        return "sell"
    if z < -Z_THRESHOLD:
        return "buy"
    return None

# ----- Main Loop -----
def main():
    logging.info("Starting mean‑reversion bot for %s", SYMBOL)
    while True:
        df = fetch_recent_bars()
        if df.empty:
            logging.warning("No data retrieved; skipping this cycle.")
            time.sleep(SLEEP_SECONDS)
            continue

        signal = evaluate_signal(df)
        if signal == "buy":
            place_order("buy")
        elif signal == "sell":
            place_order("sell")
        else:
            logging.info("No signal this round.")

        time.sleep(SLEEP_SECONDS)

if __name__ == "__main__":
    try:
        main()
    except KeyboardInterrupt:
        logging.info("Bot stopped by user.")
    except Exception as e:
        logging.exception("Unexpected error: %s", e)

Key improvements that turned the struggle into a win:

  • Defensive data fetching – we check for the expected JSON key, timeout, and wrap everything in a try/except.
  • Pandas for rolling stats – no more manual variance loops; .rolling() gives us reliable SMA and STD.
  • Logging instead of print – timestamps, levels, and debug info make troubleshooting a breeze.
  • Modular functions – each piece (data, signal, order) can be unit‑tested or swapped out.
  • Environment‑based secrets – no hard‑coded keys; keep them out of git.

Running this on a VPS (or even your laptop) with Alpaca’s paper account lets you watch the bot submit buy/sell orders in real time—no real money at risk, just pure adrenaline when you see that first order fill.

Why This New Power Matters

Now you’ve got a living, breathing algorithm that follows your rules without getting tired, scared, or greedy. Imagine extending it:

  • Add a stop‑loss or take‑profit order via Alpaca’s bracket orders.
  • Swap the mean‑reversion logic for a breakout or momentum signal—just replace evaluate_signal.
  • Pull data from multiple timeframes (e.g., 5‑minute trend filter + 1‑minute entry).
  • Deploy to a cloud scheduler (AWS Lambda, Google Cloud Run, or a simple cron job) and let it run 24/7 while you focus on learning, building side projects, or finally finishing that Netflix series.

The best part? You’ve demystified the black box. You’re not blindly trusting a “guru’s” signal; you wrote the logic, you see the data, and you control the risk. That feeling—when the bot executes a trade exactly as you planned—is like pulling the perfect swing in The Matrix and watching the bullets freeze mid‑air.

So go ahead, clone the repo, drop in your API keys, set SYMBOL to your favorite stock, and watch the magic happen.

Your Next Challenge

Pick one improvement from the list above and implement it this week. Maybe it’s adding a simple stop‑loss order, or maybe it’s pulling in a second indicator like RSI. Share your results in the comments—or better yet, write your own Dev.to post about the journey.

Remember: the market is a beast, but with a little code, discipline, and a dash of Jedi patience, you can turn it into your training ground. May the force (and good risk management) be with you! 🚀


Happy coding, and may your trades be ever in your favor!