Skip to content

Commit bd69cb8

Browse files
authored
1.0.0 (#1)
1 parent e7543eb commit bd69cb8

File tree

14 files changed

+506
-0
lines changed

14 files changed

+506
-0
lines changed

.gitignore

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
gb
2+
*.o

Makefile

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
.POSIX:
2+
.SUFFIXES:
3+
4+
include config.mk
5+
6+
CFLAGS += -std=c99 -Wall -Wextra -Wno-unused-parameter
7+
CFLAGS += -g -Iinclude -Isrc -I.
8+
9+
SRC = src
10+
OBJ = main.o book.o order.o strategy.o
11+
12+
all: gb
13+
14+
gb: $(OBJ)
15+
$(CC) -o $@ $^ $(LDFLAGS)
16+
17+
%.o: $(SRC)/%.c
18+
$(CC) $(CFLAGS) -c -o $@ $<
19+
20+
clean:
21+
rm -f gb *.o
22+
23+
install:
24+
mkdir -p $(DESTDIR)$(BINDIR)
25+
cp -f gb $(DESTDIR)$(BINDIR)
26+
chmod 755 $(DESTDIR)$(BINDIR)/gb
27+
mkdir -p $(DESTDIR)$(MANDIR)/man1
28+
cp -f gb.1 $(DESTDIR)$(MANDIR)/man1
29+
chmod 644 $(DESTDIR)$(MANDIR)/man1/gb.1
30+
31+
uninstall:
32+
rm -f $(DESTDIR)$(BINDIR)/gb
33+
rm -f $(DESTDIR)$(MANDIR)/man1/gb.1
34+
35+
.PHONY: all clean install uninstall

README.md

Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,60 @@
1+
gb
2+
==
3+
4+
*gb* is a real-time, command-line grid trading bot written in C99. It adheres to the Suckless philosophy: simple, modular, and minimal. The bot reads price data from standard input and writes order fills and status updates to standard output. All state is in-memory, and configuration is passed via command-line flags. No sockets, no threads, no dependencies.
5+
6+
Features
7+
--------
8+
9+
- Single static binary
10+
- No external libraries or socket connections
11+
- Fully pipeable: integrates with Unix tools or other processes
12+
- Designed for real-time use
13+
14+
Usage
15+
-----
16+
17+
Run:
18+
19+
./gb [options]
20+
21+
Price ticks are fed line-by-line through standard input, and order events are emitted through standard output.
22+
23+
For full command-line options and usage details, see:
24+
25+
man gb
26+
27+
Example:
28+
29+
$ echo -e "99\n100\n101" | ./gb -c 100 -l 3 -s 1 -q 0.5
30+
31+
Output:
32+
33+
TICK 1 @ 99.00
34+
Filled BUY 99.00 x 0.50
35+
36+
TICK 2 @ 100.00
37+
38+
TICK 3 @ 101.00
39+
Filled SELL 101.00 x 0.50
40+
41+
Build
42+
-----
43+
44+
make clean && make
45+
46+
47+
Produces the `./gb` binary. Requires only a POSIX-compatible C compiler (e.g., `cc` or `clang`).
48+
49+
Backtesting
50+
-----------
51+
52+
Backtesting is not part of this binary and should be done externally (e.g., via Python with `backtesting.py`).
53+
54+
Philosophy
55+
----------
56+
57+
- No bloat. All logic lives in C99 source files under 500 lines.
58+
- No runtime configuration files or databases.
59+
- No sockets, threads, JSON, XML, or other overhead.
60+
- Everything via `stdin` / `stdout`.

config.def.h

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
#ifndef CONFIG_DEF_H
2+
#define CONFIG_DEF_H
3+
4+
#define DEFAULT_CENTER 100.0
5+
#define DEFAULT_LEVELS 5
6+
#define DEFAULT_SPACING 1.0
7+
#define DEFAULT_QUANTITY 1.0
8+
9+
#define MAX_LINE_LENGTH 128
10+
11+
#endif

config.mk

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
# config.mk - default build configuration
2+
3+
PREFIX = /usr/local
4+
BINDIR = $(PREFIX)/bin
5+
MANDIR = $(PREFIX)/share/man
6+
7+
CC = cc

gb.1

Lines changed: 93 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,93 @@
1+
.TH GB 1 "July 2025" "Grid Bot 1.0" "User Commands"
2+
3+
.SH NAME
4+
gb \- real-time grid trading bot for stdin/stdout streaming
5+
6+
.SH SYNOPSIS
7+
.B gb
8+
[\-c center] [\-l levels] [\-s spacing] [\-q quantity] [\-d]
9+
10+
.SH DESCRIPTION
11+
.B gb
12+
is a lightweight, suckless-aligned, real-time grid trading bot that reads price data from standard input and emits order activity, fills, and strategy state to standard output.
13+
14+
All configuration is passed via command-line options, and the program operates as a single binary with no external dependencies, sockets, or persistent state.
15+
16+
.SH OPTIONS
17+
.TP
18+
.B \-c
19+
Set the center price for the grid (default: 100.0).
20+
.TP
21+
.B \-l
22+
Number of grid levels per side (default: 5).
23+
.TP
24+
.B \-s
25+
Grid spacing between levels (default: 1.0).
26+
.TP
27+
.B \-q
28+
Quantity per order (default: 1.0).
29+
.TP
30+
.B \-d
31+
Enable debug mode. Prints full strategy status after each tick.
32+
.TP
33+
.B \-h
34+
Print usage help and exit.
35+
36+
.SH INPUT
37+
The program expects newline-delimited price ticks via standard input:
38+
.PP
39+
.EX
40+
98.5
41+
99.0
42+
100.2
43+
.EE
44+
45+
.SH OUTPUT
46+
For each tick, gb prints a status report including:
47+
.TP
48+
.B Order fills:
49+
e.g., "Filled BUY 98.00 x 1.00"
50+
.TP
51+
.B New orders:
52+
Automatically placed grid orders above/below fill prices.
53+
.TP
54+
.B PnL and Position:
55+
Realized profit and open quantity (only shown if
56+
.B -d
57+
is specified).
58+
59+
.SH EXAMPLES
60+
Basic usage:
61+
.PP
62+
.EX
63+
$ ./gb -c 100 -l 3 -s 1 -q 1 < prices.txt
64+
.EE
65+
66+
Enable debug mode:
67+
.PP
68+
.EX
69+
$ ./gb -c 100 -l 3 -s 1 -q 1 -d
70+
.EE
71+
72+
Pipe live price data:
73+
.PP
74+
.EX
75+
$ while true; do get_price; sleep 1; done | ./gb -c 90 -l 4 -s 0.5 -q 1 -d
76+
.EE
77+
78+
.SH FILES
79+
None. All interaction is via standard input/output.
80+
81+
.SH LIMITATIONS
82+
This implementation performs no order matching, persistence, or time-aware logic. Backtesting and visualizations should be performed externally.
83+
84+
.SH SEE ALSO
85+
.BR backtesting (1),
86+
.BR awk (1),
87+
.BR python (1)
88+
89+
.SH AUTHOR
90+
Michael Czigler <[email protected]>
91+
92+
.SH LICENSE
93+
BSD 3-Clause License. See LICENSE file for details.

include/gb.h

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
#ifndef GB_H
2+
#define GB_H
3+
4+
#define _POSIX_C_SOURCE 200112L
5+
6+
#include <stdio.h>
7+
#include <stdlib.h>
8+
#include <unistd.h>
9+
#include <string.h>
10+
11+
#endif // GB_H

src/book.c

Lines changed: 68 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,68 @@
1+
#include "book.h"
2+
3+
book_t *book_create(void) {
4+
book_t *book = malloc(sizeof(book_t));
5+
if (book == NULL) return NULL;
6+
book->head = NULL;
7+
return book;
8+
}
9+
10+
void book_free(book_t *book) {
11+
if (book == NULL) return;
12+
order_t *curr = book->head;
13+
while (curr != NULL) {
14+
order_t *tmp = curr->next;
15+
order_free(curr);
16+
curr = tmp;
17+
}
18+
free(book);
19+
}
20+
21+
void book_add_order(book_t *book, order_t *order) {
22+
if ((book == NULL) || (order == NULL)) return;
23+
order->next = book->head;
24+
book->head = order;
25+
}
26+
27+
order_t * book_remove_order(book_t *book, double price,
28+
order_type_t type) {
29+
if ((book == NULL) || (book->head == NULL))
30+
return NULL;
31+
order_t *prev = NULL;
32+
order_t *curr = book->head;
33+
while (curr != NULL) {
34+
if (curr->price == price && curr->type == type) {
35+
if (prev != NULL) {
36+
prev->next = curr->next;
37+
} else {
38+
book->head = curr->next;
39+
}
40+
curr->next = NULL;
41+
return curr;
42+
}
43+
prev = curr;
44+
curr = curr->next;
45+
}
46+
47+
return NULL;
48+
}
49+
50+
int book_contains_order(book_t *book, double price,
51+
order_type_t type) {
52+
order_t *curr = book->head;
53+
while (curr != NULL) {
54+
if (curr->price == price && curr->type == type) {
55+
return 1;
56+
}
57+
curr = curr->next;
58+
}
59+
return 0;
60+
}
61+
62+
void book_print(const book_t *book) {
63+
const order_t *curr = book->head;
64+
while (curr != NULL) {
65+
order_print(curr);
66+
curr = curr->next;
67+
}
68+
}

src/book.h

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
#ifndef BOOK_H
2+
#define BOOK_H
3+
4+
#include "gb.h"
5+
#include "order.h"
6+
7+
typedef struct BOOK {
8+
order_t *head;
9+
} book_t;
10+
11+
book_t *book_create(void);
12+
void book_free(book_t *book);
13+
14+
void book_add_order(book_t *book, order_t *order);
15+
order_t *book_remove_order(book_t *book, double price,
16+
order_type_t type);
17+
int book_contains_order(book_t *book, double price,
18+
order_type_t type);
19+
void book_print(const book_t *book);
20+
21+
#endif // BOOK_H

src/main.c

Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
#include "strategy.h"
2+
#include "config.def.h"
3+
4+
static void usage(const char *prog) {
5+
fprintf(stderr,
6+
"Usage: %s [-c center] [-l levels] [-s spacing] [-q quantity] [-d]\n", prog);
7+
exit(1);
8+
}
9+
10+
int main(int argc, char *argv[]) {
11+
double center = DEFAULT_CENTER;
12+
int levels = DEFAULT_LEVELS;
13+
double spacing = DEFAULT_SPACING;
14+
double quantity = DEFAULT_QUANTITY;
15+
16+
int opt, debug = 0, tick = 0;
17+
while ((opt = getopt(argc, argv, "c:l:s:q:dh")) != -1) {
18+
switch (opt) {
19+
case 'c': center = atof(optarg); break;
20+
case 'l': levels = atoi(optarg); break;
21+
case 's': spacing = atof(optarg); break;
22+
case 'q': quantity = atof(optarg); break;
23+
case 'd': ++debug; break;
24+
case 'h': default: usage(argv[0]);
25+
}
26+
}
27+
28+
strategy_t *strategy = strategy_create(center,
29+
levels, spacing, quantity);
30+
if (strategy == NULL) {
31+
fputs("Failed to initialize strategy.\n", stderr);
32+
return 1;
33+
}
34+
35+
char line[MAX_LINE_LENGTH];
36+
while (fgets(line, sizeof(line), stdin)) {
37+
double price = atof(line);
38+
printf("\nTICK %d @ %.2f\n", ++tick, price);
39+
strategy_on_tick(strategy, price);
40+
if (debug > 0) {
41+
strategy_print_status(strategy);
42+
}
43+
fflush(stdout);
44+
}
45+
46+
strategy_free(strategy);
47+
return 0;
48+
}

0 commit comments

Comments
 (0)