AlgoDaily

Trading Lab Project Skeleton

Read at your own pace. Code execution, quizzes, progress tracking, and the AI tutor are available in the interactive view with JavaScript enabled.

Build The Lab

From toolchain to reusable project

In Course Overview & Environment Setup, you verified that your compiler, terminal, and timing smoke tests work.

Now we turn that loose setup into a small trading lab skeleton: a repeatable C++ project shape that later lessons can extend without becoming messy.

For this lesson, the goal is not to connect to an exchange or implement a strategy. The goal is to create a clean home for future pieces:

market ideas -> C++ modules -> tests -> runnable binaries

A good skeleton gives you:

  • predictable folders
  • repeatable build commands
  • simple executable targets
  • a place for shared types
  • a place for tests
  • a clean boundary between source code and generated files

This is how we keep beginner-friendly code from turning into a pile of one-off experiments.

Build The Lab

Target Lab Shape

The folder structure we want

Create a project folder named trading-lab/. Inside it, use this shape:

trading-lab/
  CMakeLists.txt
  README.md
  include/
    trading_lab/
      types.hpp
  src/
    main.cpp
  tests/
    types_test.cpp
  config/
    lab.conf
  data/
    sample_feed.txt
  build/

Each folder has a job:

  • include/trading_lab/: reusable headers shared by multiple programs
  • src/: main application code and executable entry points
  • tests/: small checks that protect behavior as the course grows
  • config/: runtime settings such as mode, symbols, or file paths
  • data/: tiny local sample files used by lab programs
  • build/: generated build files and compiled binaries

The key idea: source stays stable; build output is disposable.

Later, when we parse feed messages and reconstruct state, this structure gives every piece a natural place to live.

Target Lab Shape

First Lab Binary

Start with a boring executable

Your first src/main.cpp should do something simple and reliable:

  1. Accept a mode from the command line.
  2. Accept a symbol name.
  3. Print startup information.
  4. Reject unknown modes.

That may sound too basic, but trading systems depend on clean startup behavior. If a binary cannot clearly say what it is running, it becomes hard to debug later.

Example build/run flow:

cmake -S . -B build
cmake --build build
./build/trading_lab sim DEMO
./build/trading_lab replay DEMO

For now, sim and replay are just labels. We will give them deeper meaning as the course progresses.

```cpp
#include <chrono>
#include <iostream>
#include <string>

int main(int argc, char* argv[]) {
    const std::string mode = argc > 1 ? argv[1] : "sim";
    const std::string symbol = argc > 2 ? argv[2] : "DEMO";

    if (mode != "sim" && mode != "replay") {
        std::cerr << "[lab] invalid mode: " << mode << '\n';
        std::cerr << "[lab] expected: sim or replay\n";
        return 1;
    }

    const auto now = std::chrono::system_clock::now();
    const auto epochMs = std::chrono::duration_cast<std::chrono::milliseconds>(
        now.time_since_epoch()
    ).count();

    std::cout << "[lab] binary=trading_lab\n";
    std::cout << "[lab] mode=" << mode << '\n';
    std::cout << "[lab] symbol=" << symbol << '\n';
    std::cout << "[lab] startup_epoch_ms=" << epochMs << '\n';
    std::cout << "[lab] status=ready\n";

    return 0;
}
```

Build Folder Check

You are organizing trading-lab/ and need a place for generated compiler output, CMake files, and binaries.

Why should these usually live in build/ instead of being mixed into src/?

Shared Domain Types

Create tiny shared types early

A lab skeleton should have a place for reusable ideas before the project becomes large.

For example, include/trading_lab/types.hpp can eventually hold small domain types used by multiple programs. At this stage, keep it humble: instrument names, integer price ticks, and quantity types.

Why use integer ticks in a trading lab?

  • Prices often move in fixed increments.
  • Integer comparisons are predictable.
  • Tests are easier to write.
  • You avoid surprising floating-point equality checks in core logic.

The example below is standalone so you can compile it directly while learning. In the project, the type definitions would move into a header.

```cpp
#include <cmath>
#include <cstdint>
#include <iomanip>
#include <iostream>
#include <string>

using PriceTicks = std::int64_t;
using Quantity = std::int64_t;

struct Instrument {
    std::string symbol;
    int priceScale; // 100 means cents, 1000 means mills
};

PriceTicks toTicks(double price, int scale) {
    return static_cast<PriceTicks>(std::llround(price * scale));
}

double fromTicks(PriceTicks ticks, int scale) {
    return static_cast<double>(ticks) / static_cast<double>(scale);
}

int main() {
    Instrument demo{"DEMO", 1000};
    const double displayedPrice = 100.125;
    const PriceTicks ticks = toTicks(displayedPrice, demo.priceScale);
    const Quantity qty = 250;

    std::cout << std::fixed << std::setprecision(3);
    std::cout << "symbol=" << demo.symbol << '\n';
    std::cout << "displayed_price=" << displayedPrice << '\n';
    std::cout << "price_ticks=" << ticks << '\n';
    std::cout << "round_trip_price=" << fromTicks(ticks, demo.priceScale) << '\n';
    std::cout << "quantity=" << qty << '\n';

    return 0;
}
```

One File Myth

True or false: A beginner trading lab should stay as one giant C++ file for as long as possible because folders and headers only matter in production.

Tiny Regression Test

Add a small test executable

You do not need a full testing framework yet. A tiny tests/types_test.cpp program is enough to prove the idea:

  • run a few checks
  • print pass/fail output
  • return nonzero on failure

This matters because trading code changes constantly. Every refactor should answer one question:

Did we preserve the behavior we already trusted?

The test below checks our price tick conversion. In a real project, your build system would compile this as a separate test target.

```cpp
#include <cmath>
#include <cstdint>
#include <iostream>
#include <string>

using PriceTicks = std::int64_t;

PriceTicks toTicks(double price, int scale) {
    return static_cast<PriceTicks>(std::llround(price * scale));
}

int recordCheck(bool condition, const std::string& name) {
    if (condition) {
        std::cout << "PASS " << name << '\n';
        return 0;
    }

    std::cout << "FAIL " << name << '\n';
    return 1;
}

int main() {
    int failures = 0;

    failures += recordCheck(toTicks(100.125, 1000) == 100125, "mills conversion");
    failures += recordCheck(toTicks(42.19, 100) == 4219, "cents conversion");
    failures += recordCheck(toTicks(0.01, 100) == 1, "one cent conversion");

    if (failures == 0) {
        std::cout << "All type checks passed.\n";
        return 0;
    }

    std::cout << failures << " check(s) failed.\n";
    return 1;
}
```

Folder Name Blank

Fill in the blank:

Generated CMake files, object files, and compiled binaries should usually go in the ____/ directory so they can be deleted and recreated safely.

Commit The Boundary

Know what belongs in Git

A trading lab should be reproducible. That means the important human-written files should be committed, while generated files should stay out.

Usually commit:

  • CMakeLists.txt
  • README.md
  • include/
  • src/
  • tests/
  • small config/ examples
  • tiny sample files in data/ when they are part of the lesson

Usually ignore:

  • build/
  • compiled binaries
  • object files
  • editor caches
  • large downloaded datasets
  • machine-specific temporary files

A useful mental model:

Can I recreate it from source commands?  -> ignore it
Did I write it or need it for the lesson? -> commit it

Before moving into Market Microstructure & Data Feeds, make sure your lab can build, run, and survive a clean rebuild.

Commit The Boundary

Skeleton Readiness Check

Before continuing to the next Market Foundations lesson, which checklist best describes a ready trading lab skeleton?

One Pager Cheat Sheet

One Pager Cheat Sheet

  • A trading lab skeleton turns one-off C++ experiments into a repeatable project that future lessons can extend.
  • Keep reusable headers, executable source, tests, config, data, and generated build output in separate folders.
  • A first lab executable should start predictably, print its runtime settings, and fail clearly on invalid input.
  • Generated build output belongs in a disposable build/ directory, not mixed with source files.
  • Shared trading types should be small, explicit, and easy to test; integer ticks are often safer than raw floating-point prices.
  • Even beginner trading code benefits from a simple structure before the project grows.
  • A tiny test executable can protect important behavior before you introduce heavier tooling.
  • Use build/ as the disposable directory for generated files and compiled binaries.
  • Commit source and lesson inputs; ignore generated artifacts that can be recreated from build commands.
  • A ready early lab has structure, repeatable builds, a runnable binary, shared types, and a small test.
Open interactive view →