Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
29 changes: 29 additions & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
name: CI

on:
push:
branches: [main]
pull_request:
branches: [main]

jobs:
build-and-test:
runs-on: ubuntu-latest

steps:
- uses: actions/checkout@v4

- name: Setup .NET 10
uses: actions/setup-dotnet@v4
with:
dotnet-version: '10.0.x'
dotnet-quality: 'preview'

- name: Restore
run: dotnet restore

- name: Build
run: dotnet build --no-restore --configuration Release

- name: Test
run: dotnet test --no-build --configuration Release --verbosity normal
44 changes: 44 additions & 0 deletions .github/workflows/publish.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
name: Publish NuGet Packages

on:
push:
tags: ['v*']
workflow_dispatch:

jobs:
publish:
runs-on: ubuntu-latest

steps:
- uses: actions/checkout@v4

- name: Setup .NET 10
uses: actions/setup-dotnet@v4
with:
dotnet-version: '10.0.x'
dotnet-quality: 'preview'

- name: Compute version
id: version
run: |
YEAR=$(date -u +%Y)
MONTH=$(date -u +%-m)
DAY=$(date -u +%-d)
VERSION="${YEAR}.${MONTH}.${DAY}.${GITHUB_RUN_NUMBER}"
echo "version=${VERSION}" >> "$GITHUB_OUTPUT"
echo "Computed version: ${VERSION}"

- name: Restore
run: dotnet restore

- name: Build
run: dotnet build --no-restore --configuration Release /p:Version=${{ steps.version.outputs.version }}

- name: Test
run: dotnet test --no-build --configuration Release --verbosity normal

- name: Pack
run: dotnet pack --no-build --configuration Release /p:Version=${{ steps.version.outputs.version }} --output ./nupkgs

- name: Push to NuGet
run: dotnet nuget push ./nupkgs/*.nupkg --api-key ${{ secrets.NUGET_API_KEY }} --source https://api.nuget.org/v3/index.json --skip-duplicate
41 changes: 41 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
# .NET build
bin/
obj/
*.csproj.user
*.rsuser
*.suo
*.user
.vs/

# NuGet
*.nupkg
*.snupkg

# C++ (cross-language)
cross-language/cpp/build/

# Node.js (cross-language)
cross-language/typescript/node_modules/

# Python
__pycache__/
*.pyc
.venv/
venv/

# Test results & coverage
TestResults/
coverage/

# Publish output
**/publish/

# OS
.DS_Store
Thumbs.db

# BenchmarkDotNet
BenchmarkDotNet.Artifacts/

# JetBrains
.idea/
21 changes: 21 additions & 0 deletions Directory.Build.props
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
<Project>
<PropertyGroup>
<!-- Version is injected by CI via /p:Version=...; fallback for local dev -->
<Version>0.0.0-local</Version>
<Authors>Daniel Bunting</Authors>
<Company>Daniel Bunting</Company>
<PackageLicenseExpression>Apache-2.0</PackageLicenseExpression>
<PackageReadmeFile>README.md</PackageReadmeFile>
<RepositoryUrl>https://github.com/DanielBunting/Levels</RepositoryUrl>
<RepositoryType>git</RepositoryType>
<PackageProjectUrl>https://github.com/DanielBunting/Levels</PackageProjectUrl>
<Description>Levels - a multi-level time-series database for financial market data</Description>
<PackageTags>timeseries;database;financial;market-data;orderbook;ohlcv</PackageTags>
<Copyright>Copyright (c) Daniel Bunting</Copyright>
</PropertyGroup>

<ItemGroup>
<None Include="$(MSBuildThisFileDirectory)README.md" Pack="true" PackagePath="/" />
<None Include="$(MSBuildThisFileDirectory)LICENSE" Pack="true" PackagePath="/" />
</ItemGroup>
</Project>
328 changes: 328 additions & 0 deletions Levels.sln

Large diffs are not rendered by default.

100 changes: 100 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,100 @@
# Levels

A multi-level time-series database for financial market data -- orderbook events, price streaming, and OHLCV bars. Built on .NET 10.0 with cross-language support for C++, Python, and TypeScript.

## Features

- **Custom binary format** -- 56-byte core records with schema-extensible fields, CRC32 integrity, little-endian layout
- **Three operational modes** -- embed in C# apps, run as a standalone TCP server, or deploy as a REST API
- **Automatic data lifecycle** -- compaction, period promotion with data quality checks, configurable retention and archival
- **Cross-language support** -- generate readers/writers for C#, Python, TypeScript, and C++ from a single `.fbs` schema
- **Real-time and batch** -- OHLCV bar emission, top-of-book snapshots, orderbook L1/L2/L3 projection
- **Export** -- CSV, Parquet, and Avro adapters
- **Observability** -- OpenTelemetry metrics and tracing, Prometheus scraping endpoint

## Prerequisites

- [.NET 10.0 SDK](https://dotnet.microsoft.com/download/dotnet/10.0) (preview)

## Quick Start

```bash
# Build
dotnet build

# Run tests
dotnet test

# Run the sample (writes synthetic orderbook data)
dotnet run --project samples/CryptoExchangeSample
```

See the [Quickstart Guide](docs/quickstart.md) for a full walkthrough.

## Operational Modes

| Mode | Use case | Entry point |
|------|----------|-------------|
| [Embedded](docs/embedded-mode.md) | In-process C# ingestion | `AddLevels<TSchema>()` + `AddLevelsDataSink<T>()` |
| [TCP Server](docs/server-mode.md) | Standalone daemon, multi-language clients | `src/Levels.Server` with YAML config |
| [REST API](docs/web-api.md) | HTTP queries and export | `src/Levels.Web` with ASP.NET Core |

## CLI

The `levels` CLI tool provides file inspection, compaction, code generation, resampling, and data export.

```bash
dotnet run --project src/Levels.Cli -- --help
```

See the [CLI Reference](docs/cli.md) for all commands.

## Documentation

- [Quickstart](docs/quickstart.md) -- get up and running
- [Architecture](docs/architecture.md) -- data pipeline, binary format, system internals
- [Embedded Mode](docs/embedded-mode.md) -- in-process C# usage
- [Server Mode](docs/server-mode.md) -- standalone TCP server
- [Web API](docs/web-api.md) -- REST endpoints
- [Schemas](docs/schemas.md) -- FlatBuffers schema system and code generation
- [Configuration](docs/configuration.md) -- complete options reference
- [CLI](docs/cli.md) -- command-line tool reference
- [Cross-Language](docs/cross-language.md) -- C++, Python, TypeScript interop

## Project Structure

```
Levels/
src/
Levels.Core/ Core binary format, interfaces, WAL, diagnostics
Levels.SourceGen/ Roslyn source generator for .fbs schemas (netstandard2.0)
Levels.Sinks/ Partitioned ingestion, circuit breaker, backpressure
Levels.DataFlow/ Channel-based async pub/sub bus
Levels.Query/ In-memory file index, orderbook projection
Levels.Compaction/ Event-sourcing replay, synthetic snapshots, archival
Levels.Period/ Data quality checks, promotion/demotion
Levels.Resampled/ OHLCV bars, top-of-book snapshots
Levels.Export/ CSV, Parquet, Avro adapters
Levels.Hosting/ DI registration (AddLevels), OpenTelemetry setup
Levels.Hints/ LiteDB-based query hints
Levels.Protocol/ Wire protocol (frame reader/writer)
Levels.Server/ Standalone TCP server
Levels.Client/ TCP client library
Levels.Web/ ASP.NET Core REST API
Levels.Cli/ Command-line tool
samples/
CryptoExchangeSample/ Simulated exchange (synthetic data)
BinanceLiveSample/ Live Binance WebSocket feed
tests/
Levels.Tests/ xUnit tests + cross-language round-trips
Levels.Benchmarks/ BenchmarkDotNet performance suite
cross-language/
schema.fbs Canonical schema definition
cpp/ C++ generated code and tests
python/ Python generated code and tests
typescript/ TypeScript generated code and tests
```

## License

Apache-2.0
7 changes: 7 additions & 0 deletions cross-language/cpp/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
cmake_minimum_required(VERSION 3.14)
project(aiondb_cross_language_cpp LANGUAGES CXX)

set(CMAKE_CXX_STANDARD 17)
set(CMAKE_CXX_STANDARD_REQUIRED ON)

add_executable(write_test_file write_test_file.cpp)
89 changes: 89 additions & 0 deletions cross-language/cpp/record.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,89 @@
// <auto-generated/>
// Generated from .fbs schema — do not edit by hand.

#pragma once

#include <cstdint>
#include <cstring>

// ── Constants ──────────────────────────────────────────────────────────────────
static const int HEADER_SIZE = 128;
static const int FOOTER_SIZE = 64;
static const int CORE_RECORD_SIZE = 56;
static const int RECORD_SIZE = 88;
static const int EXTENSION_SIZE = 32;
static const uint16_t FORMAT_VERSION = 2;
static const uint32_t SCHEMA_ID = 0x5D1FE1FDu;

// Extension field offsets (relative to record start)
static const int ORDER_ID_OFFSET = 56;
static const int ORDER_ID_SIZE = 32;

// ── Binary layout structs (packed, little-endian) ─────────────────────────────

#pragma pack(push, 1)

struct FileHeader {
uint8_t magic[8]; // "LEVELS01"
uint16_t version; // FORMAT_VERSION
uint8_t file_type; // 0 = Raw
uint8_t padding; // 0
uint32_t schema_id;
int64_t price_stream_id;
int64_t created_at;
int32_t price_scale;
int32_t quantity_scale;
uint32_t resampled_config_hash;
uint16_t record_size; // total bytes per record
uint8_t reserved[82]; // zeros
};
static_assert(sizeof(FileHeader) == 128, "FileHeader must be 128 bytes");

struct CoreRecord {
int64_t observed_time; // 0
int64_t write_timestamp; // 8
int64_t price_stream_id; // 16
int64_t price; // 24
int64_t quantity; // 32
uint16_t _reserved; // 40
uint8_t record_type; // 42
uint8_t record_side; // 43
uint32_t sequence; // 44
uint16_t level; // 48
uint16_t flags; // 50
uint32_t crc32; // 52
};
static_assert(sizeof(CoreRecord) == 56, "CoreRecord must be 56 bytes");

struct FullRecord {
int64_t observed_time; // 0
int64_t write_timestamp; // 8
int64_t price_stream_id; // 16
int64_t price; // 24
int64_t quantity; // 32
uint16_t _reserved; // 40
uint8_t record_type; // 42
uint8_t record_side; // 43
uint32_t sequence; // 44
uint16_t level; // 48
uint16_t flags; // 50
uint32_t crc32; // 52
uint8_t order_id[32]; // 56
};
static_assert(sizeof(FullRecord) == 88, "FullRecord must be 88 bytes");

struct FileFooter {
int64_t record_count; // 0
int64_t delta_count; // 8
int64_t first_write_timestamp; // 16
int64_t last_write_timestamp; // 24
int64_t first_observed_time; // 32
int64_t last_observed_time; // 40
uint32_t file_crc32; // 48
uint8_t padding[4]; // 52
uint8_t magic_end[8]; // 56 "LEVEND01"
};
static_assert(sizeof(FileFooter) == 64, "FileFooter must be 64 bytes");

#pragma pack(pop)

Binary file added cross-language/cpp/write_test_file
Binary file not shown.
Loading
Loading