A trading environment that is not reproducible is a liability. When something breaks at 9:31am, "I think it was installed correctly" is not a useful debugging state. This guide produces an environment you can rebuild from scratch in under fifteen minutes and verify with a single script. Every dependency is pinned, every credential is isolated, and the final smoke test leaves no ambiguity about whether the setup is correct.

Prerequisites

Before starting, confirm you have the following:

  • Python 3.10 or higher (3.11 recommended — it is the version this guide pins)
  • git — for version control from the first commit
  • A terminal — zsh or bash; this guide is terminal-native throughout
  • An Alpaca account — paper trading is free at alpaca.markets; no funding required

No IDE is required. Every command in this guide runs in a terminal. The setup is intentionally stripped of GUI tooling so that it works identically on a local machine, a remote VPS, or a CI pipeline.

Step 1 — Python Version Management with pyenv

The standard mistake is using the system Python. System Python is shared across all processes on the machine — OS tools, package managers, other projects. Changing the system Python version breaks things you did not intend to change. On macOS, for example, several macOS system utilities depend on the Python bundled with the OS. On Linux, package manager scripts sometimes assume a specific Python version.

pyenv solves this by maintaining a directory of Python installations and switching between them per-project using a small shim layer. It does not touch the system Python installation at all.

Install pyenv:

curl https://pyenv.run | bash

After installation completes, add the following to your shell profile (.zshrc for zsh, .bashrc for bash):

export PYENV_ROOT="$HOME/.pyenv"
export PATH="$PYENV_ROOT/bin:$PATH"
eval "$(pyenv init -)"

Restart your terminal or run source ~/.zshrc to apply the changes. Then install Python 3.11 and set it as the local version for this project:

pyenv install 3.11.0
pyenv local 3.11.0
python --version  # should output Python 3.11.0

The pyenv local command writes a .python-version file to the current directory. Any terminal opened in that directory picks up 3.11.0 automatically — no activation command required. This file should be committed to version control so that anyone cloning the project gets the same Python version without manual configuration.

If pyenv install 3.11.0 fails on Linux, the missing dependency is usually a build tool or header library. Run pyenv install --list | grep 3.11 to confirm the version is available, then check the pyenv wiki for the dependency list for your distribution. On macOS, installing Xcode command-line tools (xcode-select --install) resolves the majority of build failures.

Step 2 — Project Structure

A clean structure avoids the common failure mode of strategies, data files, and credentials scattered across the home directory. When everything is co-located without separation, debugging a data pipeline error requires first figuring out which file you are even looking at. A consistent structure also makes it possible to apply the same permissions model, the same backup policy, and the same .gitignore patterns across every trading project without rethinking the layout each time.

The structure below separates concerns cleanly:

trading/
├── strategies/          # strategy modules
├── data/                # local data cache
├── logs/                # execution logs
├── config/              # YAML config files (committed to version control)
├── .env                 # API keys — never committed
├── .gitignore           # includes .env, data/, logs/
└── requirements.txt

Create it and initialize version control:

mkdir -p trading/{strategies,data,logs,config}
cd trading
git init
echo ".env" >> .gitignore
echo "data/" >> .gitignore
echo "logs/" >> .gitignore

The data/ and logs/ directories are in .gitignore because their contents are ephemeral — regenerable from the API or from execution runs. Committing them pollutes the repository history with binary data and large CSVs. The config/ directory is committed because YAML configuration files describe strategy behavior; they are part of the codebase, not runtime artifacts.

Step 3 — Virtual Environment and Core Libraries

A virtual environment isolates the project's Python dependencies from every other Python environment on the machine. Without one, running pip install modifies the global Python installation — and eventually two projects require incompatible versions of the same library. The conflict resolution between incompatible global packages can silently break code in ways that are time-consuming to debug because the error surfaces far from the root cause.

python -m venv venv
source venv/bin/activate  # on Windows: venv\Scriptsctivate

Your terminal prompt should now show (venv) as a prefix, confirming the environment is active. Install the core libraries:

pip install alpaca-py pandas numpy python-dotenv requests
pip freeze > requirements.txt

What each library provides:

  • alpaca-py — Alpaca's official Python client for trading and market data
  • pandas — price data manipulation, time-series operations, DataFrame handling
  • numpy — numerical operations underlying most quantitative calculations
  • python-dotenv — loads key-value pairs from a .env file into environment variables
  • requests — general-purpose HTTP client for any API not covered by a dedicated SDK

The pip freeze > requirements.txt step locks every installed package and its exact version. Anyone cloning the project and running pip install -r requirements.txt gets an identical environment. This is the reproducibility guarantee. Note that pip freeze includes transitive dependencies — packages that alpaca-py or pandas depend on — not just the five you explicitly installed. That is intentional; the goal is bit-for-bit reproducibility.

If you add a new library later, run pip freeze > requirements.txt again to update the lock file. Commit the updated file alongside the code change that required the new dependency so that the reason for the change is traceable in version history.

Step 4 — Alpaca API Credentials

Alpaca provides two separate environments: paper trading (simulated, free) and live trading (real capital). This guide uses paper trading throughout. The API keys for each environment are entirely separate — a paper key cannot interact with a live account, and vice versa. This separation is deliberate and is one of the reasons Alpaca's paper environment is trustworthy for full end-to-end testing.

  1. Create a free account at alpaca.markets if you have not already
  2. Navigate to Paper Trading in the left sidebar, then API Keys
  3. Click Generate New Key and copy both the API key and the secret key immediately — the secret is shown only once and cannot be retrieved after the modal closes

Create a .env file in your project root:

ALPACA_API_KEY=your_key_here
ALPACA_SECRET_KEY=your_secret_here
ALPACA_PAPER=true

Verify that .env is in .gitignore:

cat .gitignore

It should include .env on its own line. If you accidentally commit credentials to a repository — even a private one — treat them as compromised and rotate them immediately. Alpaca's key rotation takes effect within seconds; there is no reason to delay.

The ALPACA_PAPER=true variable is not read by alpaca-py directly — it is a self-documenting marker for your code. When you write the client initialization, you will read this variable to set the paper=True parameter. This pattern makes it possible to change from paper to live trading by changing one environment variable value rather than modifying source code.

Step 5 — Smoke Test

The smoke test confirms that credentials are loaded correctly, the API client initializes without errors, and the Alpaca API is reachable. Create verify.py in the project root:

import os
from dotenv import load_dotenv
from alpaca.trading.client import TradingClient

load_dotenv()

client = TradingClient(
    api_key=os.getenv("ALPACA_API_KEY"),
    secret_key=os.getenv("ALPACA_SECRET_KEY"),
    paper=True
)

account = client.get_account()
print(f"Account status:  {account.status}")
print(f"Buying power:    ${float(account.buying_power):,.2f}")
print(f"Portfolio value: ${float(account.portfolio_value):,.2f}")

Run it:

python verify.py

Expected output shows Account status: ACTIVE and buying power of $100,000 — the paper account default. If the script throws an AuthenticationError, the key or secret is incorrect or the .env file is in the wrong directory. If it throws a ConnectionError, check your network and confirm the Alpaca API status page at status.alpaca.markets.

A clean run here means the entire credential chain works: the .env file is in the right location, python-dotenv loads it correctly, the API client can authenticate, and the Alpaca servers are responding. Any failure at this stage is easier to diagnose now than embedded inside a strategy that is also managing positions.

Step 6 — Confirm Data Access

The trading client handles order management and account operations. A separate client, StockHistoricalDataClient, handles market data. The two are intentionally separate in alpaca-py's design — they authenticate differently and connect to different API endpoints. Confirm data access with a small historical bar request:

from alpaca.data.historical import StockHistoricalDataClient
from alpaca.data.requests import StockBarsRequest
from alpaca.data.timeframe import TimeFrame
from datetime import datetime

data_client = StockHistoricalDataClient()  # no auth required for free tier

request = StockBarsRequest(
    symbol_or_symbols=["AAPL"],
    timeframe=TimeFrame.Day,
    start=datetime(2024, 1, 1),
    end=datetime(2024, 3, 31)
)
bars = data_client.get_stock_bars(request).df
print(bars.head())

The result is a pandas DataFrame with columns for open, high, low, close, volume, and trade count. Seeing five rows of OHLCV data confirms the data feed is accessible. If the result is an empty DataFrame, check that the requested date range does not fall entirely on market holidays.

StockHistoricalDataClient() is instantiated with no arguments for the free tier. Free-tier access covers historical daily bars without authentication. Authenticated requests are needed for real-time streaming data and intraday bar data at minute resolution — both of which require an Alpaca subscription tier above free. For daily-bar backtesting and the strategy patterns covered in this series, the free tier is sufficient.

The Oyamori Approach

This environment is the foundation every subsequent article in this series builds on. Oyamori's CLI tool runs in this same Python environment and adds the execution and strategy management layer on top — the same pyenv-managed Python version, the same virtual environment pattern, the same .env credential loading.

All Oyamori configuration follows a consistent pattern: YAML files in config/ committed to version control, API credentials in .env excluded from version control. The separation is not arbitrary — it makes the configuration auditable and the credentials replaceable without touching the codebase. When a key is rotated, the .env file changes and the codebase does not.


Next: Your First Live Trade via API →