GeezerVisonThe World Wide Web, restored to 800 pixels of glory. | |||||||||||||||||||||||||||
| Home|About|Gaming cabinet|Archives|How it works|Starter sites | |||||||||||||||||||||||||||
GeezerVison is showing cdocs.pineforge.dev — PineForge: Tutorial — MACD on BTCUSDT 15m
Loading...
Searching...
No Matches
Tutorial — MACD on BTCUSDT 15m Table of ContentsEnd-to-end backtest you can reproduce from a fresh clone in under a minute. Source: What this tutorial covers
Re-running with other parameters, sweeping a grid and running strategies in parallel are the example pages at the end. By the end you'll have the canonical patterns for ad-hoc backtests, parameter sweeps, walk-forward windows, and live diagnostic capture. Layouttutorial/
├── macd/
│ ├── strategy.pine # PineScript v6 reference
│ └── generated.cpp # transpiled C++ → becomes strategy.so
├── mtf/ # two MTF strategies (see Multi-timeframe)
├── data/
│ ├── btcusdt_15m_7d.csv # 672 frozen bars (Binance)
│ └── fetch_btcusdt.py # refresh from Binance public API
├── run.py # ctypes harness
├── run_stream.py # historical OHLCV → realtime trade stream
├── run_advanced.py # parameter sweep using ABI overrides
├── run_mtf.py # MTF demo: script_tf switch + lower_tf
├── run.sh # one-shot: cmake build + run.py
└── CMakeLists.txt
The Pine source//@version=6
strategy("MACD Crossover (tutorial)",
overlay = false,
initial_capital = 1000000,
currency = currency.USD,
process_orders_on_close = false,
pyramiding = 1,
commission_type = strategy.commission.percent,
commission_value = 0,
slippage = 0,
default_qty_type = strategy.fixed,
default_qty_value = 1)
fastLen = input.int(12, "Fast Length", minval=1)
slowLen = input.int(26, "Slow Length", minval=1)
signalLen = input.int(9, "Signal Length", minval=1)
src = input.source(close, "Source")
[macdLine, signalLine, histLine] = ta.macd(src, fastLen, slowLen, signalLen)
longCond = ta.crossover(macdLine, signalLine)
shortCond = ta.crossunder(macdLine, signalLine)
if longCond
strategy.entry("Long", strategy.long)
if shortCond
strategy.entry("Short", strategy.short)
Path A — local toolchainRequires bash tutorial/run.sh
Configures CMake (first time only), builds MACD(12,26,9) on BTCUSDT 15m — 672 bars, 2026-04-29 18:15 → 2026-05-06 18:00 UTC
trades: 50 (17W / 33L, 34.0% win)
net pnl: +569.97
best/worst:+1149.00 / -1111.97
max dd: -4045.15
elapsed: 0.4 ms
Numbers depend on the OHLCV snapshot — refresh with Path B — DockerMount the strategy + OHLCV into the release hub's image, docker run --rm \
-v "$(pwd)/tutorial/macd/strategy.pine:/in/strategy.pine:ro" \
-v "$(pwd)/tutorial/data/btcusdt_15m_7d.csv:/in/ohlcv.csv:ro" \
ghcr.io/pineforge-4pass/pineforge-release:latest > report.json
jq '.summary' report.json
The image transpiles the Inside run.py — annotated walkthroughThe full harness is ~200 lines. Here's the dataflow, end to end. 1. Mirror the C ABI in ctypesSkipped here — see FFI from Python for the ctypes mirror of the report structs. The harness defines 2. Load OHLCV into a contiguous arraywith OHLCV.open(newline="") as f:
rows = list(csv.DictReader(f))
n = len(rows)
bars = (BarC * n)()
for i, r in enumerate(rows):
bars[i] = BarC(float(r["open"]), float(r["high"]), float(r["low"]),
float(r["close"]), float(r["volume"]), int(r["timestamp"]))
3. Wire the symbol tablelib = ctypes.CDLL(str(SO))
lib.strategy_create.argtypes = [ctypes.c_char_p]
lib.strategy_create.restype = ctypes.c_void_p
lib.run_backtest_full.argtypes = [
ctypes.c_void_p, ctypes.POINTER(BarC), ctypes.c_int,
ctypes.c_char_p, ctypes.c_char_p,
ctypes.c_int, ctypes.c_int, ctypes.c_int,
ctypes.POINTER(ReportC)]
lib.strategy_free.argtypes = [ctypes.c_void_p]
lib.report_free.argtypes = [ctypes.POINTER(ReportC)]
4. Runstate, report = lib.strategy_create(b"{}"), ReportC()
t0 = time.time()
lib.run_backtest_full(state, bars, n,
b"", # input_tf — auto-detect
b"", # script_tf — same as input
0, 4, 3, # magnifier off, 4 samples, ENDPOINTS
ctypes.byref(report))
elapsed = time.time() - t0
5. Read the reportpnls = [report.trades[i].pnl for i in range(report.trades_len)]
wins, losses = sum(p > 0 for p in pnls), sum(p < 0 for p in pnls)
cum = peak = max_dd = 0.0
for p in pnls:
cum += p
peak = max(peak, cum)
max_dd = min(max_dd, cum - peak)
print(f" trades: {report.trades_len} ({wins}W / {losses}L)")
print(f" net pnl: {report.net_profit:+.2f}")
print(f" max dd: {max_dd:.2f}")
6. Freelib.report_free(ctypes.byref(report))
lib.strategy_free(state)
Order matters — see Lifecycle § Free everything. More worked examplesThe pages below pick up where this tutorial leaves off — each one is a self-contained, runnable example targeting a specific use case.
What to read next
| |||||||||||||||||||||||||||
| You are visitor 01402503 — GeezerVison does not store the pages you look up. Best viewed with Netscape Navigator 4.7 or Internet Explorer 6 at 800×600. No cookies required. |