package main import ( "encoding/json" "fmt" "os" ) // Config holds all tunable parameters for the ingest engine. type Config struct { // WebSocket endpoint for Bybit V5 public linear trades. WebSocketURL string `json:"websocket_url"` // Symbol to subscribe to. Symbol string `json:"symbol"` // DataDir is the base directory for all database files. DataDir string `json:"data_dir"` // LogFile is the optional path to a file where logs will be written. LogFile string `json:"log_file"` // HotRetentionHours is how many hours of raw ticks to keep in the hot DB. HotRetentionHours int `json:"hot_retention_hours"` // FeatureRetentionDays is how many days of 5s features to keep. FeatureRetentionDays int `json:"feature_retention_days"` // WriterFlushIntervalMs is how often (ms) the batch writer flushes to disk. WriterFlushIntervalMs int `json:"writer_flush_interval_ms"` // WriterBatchSize is the max number of ticks before a forced flush. WriterBatchSize int `json:"writer_batch_size"` // TickChannelBuffer is the capacity of the tick channel between WS reader and DB writer. TickChannelBuffer int `json:"tick_channel_buffer"` // MaintenanceIntervalMinutes controls how often the hourly maintenance runs. MaintenanceIntervalMinutes int `json:"maintenance_interval_minutes"` } // DefaultConfig returns a Config populated with sensible defaults. func DefaultConfig() Config { return Config{ WebSocketURL: "wss://stream.bybit.com/v5/public/linear", Symbol: "BTCUSDT", DataDir: "data", LogFile: "engine.log", HotRetentionHours: 12, FeatureRetentionDays: 30, WriterFlushIntervalMs: 500, WriterBatchSize: 100, TickChannelBuffer: 10000, MaintenanceIntervalMinutes: 60, } } // LoadConfig reads a JSON config file and merges with defaults. func LoadConfig(path string) (Config, error) { cfg := DefaultConfig() data, err := os.ReadFile(path) if err != nil { if os.IsNotExist(err) { if writeErr := writeDefaultConfig(path, cfg); writeErr != nil { return cfg, fmt.Errorf("failed to write default config: %w", writeErr) } fmt.Printf("No config found, wrote defaults to %s\n", path) return cfg, nil } return cfg, fmt.Errorf("failed to read config: %w", err) } if err := json.Unmarshal(data, &cfg); err != nil { return cfg, fmt.Errorf("failed to parse config: %w", err) } return cfg, nil } func writeDefaultConfig(path string, cfg Config) error { data, err := json.MarshalIndent(cfg, "", " ") if err != nil { return err } return os.WriteFile(path, data, 0644) }