Skip to content

sukruozdemir/chartwise-api-go

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

16 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

ChartWise API

A high-performance, clean-architecture REST API built with Go and Gin framework for accessing cryptocurrency exchange data through the CCXT (CryptoCurrency eXchange Trading) library. Designed for real-time market data retrieval, symbol management, and OHLCV (Open, High, Low, Close, Volume) candle data across multiple crypto exchanges.

Features

  • Multi-Exchange Support: Connect to 100+ cryptocurrency exchanges via CCXT
  • Smart Caching: Built-in caching layer with configurable TTL to reduce API calls
  • Rate Limiting: Request rate limiting to prevent service overload
  • OHLCV Data: Historical candlestick data with parallel fetch optimization
  • Health Checks: Kubernetes-ready health check endpoints
  • CORS Support: Configurable cross-origin request handling
  • Compression: Automatic response compression for bandwidth optimization
  • Structured Logging: Zap-based JSON/console logging with configurable levels
  • Error Handling: Centralized error handling with appropriate HTTP status codes
  • Request Tracking: Unique request IDs for request tracing

Architecture

ChartWise follows a strict clean architecture pattern with clear layer separation:

HTTP Request
    ↓
Middleware Pipeline (Recovery, Logger, CORS, Rate Limit, Timeout, etc.)
    ↓
Router (Request routing and parameter validation)
    ↓
Handler Layer (Parse request, validate input, call services)
    ↓
Service Layer (Business logic, caching, data transformation)
    ↓
External APIs (CCXT for exchange data)

Directory Structure

chartwise-api-go/
├── cmd/
│   ├── api/
│   │   └── main.go              # Application entry point
│   └── bench/
│       └── main.go              # Benchmarking tools
├── internal/
│   ├── config/
│   │   └── config.go            # Configuration management (Viper)
│   ├── dto/
│   │   ├── common.go            # Common DTOs
│   │   └── exchange.go          # Exchange-specific DTOs
│   ├── handler/
│   │   ├── health.go            # Health check handlers
│   │   └── exchange.go          # Exchange endpoint handlers
│   ├── middleware/
│   │   ├── recovery.go          # Panic recovery
│   │   ├── request_id.go        # Request ID generation
│   │   ├── logger.go            # Structured logging
│   │   ├── cors.go              # CORS handling
│   │   ├── compression.go       # Response compression
│   │   ├── response.go          # Response wrapping
│   │   ├── timeout.go           # Request timeout
│   │   ├── ratelimit.go         # Rate limiting
│   │   └── validator.go         # Input validation
│   ├── router/
│   │   └── router.go            # Route registration
│   └── services/
│       ├── health_service.go    # Health check logic
│       ├── exchange_service.go  # Exchange operations
│       └── exchange_service_parallel.go # Parallel data fetching
├── pkg/
│   ├── errors/
│   │   └── errors.go            # Centralized error types
│   ├── logger/
│   │   └── logger.go            # Zap-based logger wrapper
│   └── utils/
│       ├── string.go            # String utilities
│       ├── map.go               # Map utilities
│       └── validator.go         # Validation utilities
├── go.mod                       # Module definition
├── go.sum                       # Dependency checksums
└── .env.example                 # Example environment configuration

Prerequisites

  • Go 1.25.5 or higher

Installation

Local Setup

  1. Clone the repository:
git clone https://github.com/sukruozdemir/chartwise-api-go.git
cd chartwise-api-go
  1. Install dependencies:
go mod download
  1. Create environment configuration:
cp .env.example .env
  1. Build the application:
go build -o bin/api ./cmd/api/

Running the Application

Development Mode

go run ./cmd/api/main.go

The server will start on http://localhost:8080 (configurable via APP_PORT).

Production Build

go build -o bin/api ./cmd/api/
./bin/api

API Endpoints

Health Checks

GET /healthz

Liveness probe endpoint (Kubernetes standard)

curl http://localhost:8080/healthz

GET /readyz

Readiness probe endpoint (Kubernetes standard)

curl http://localhost:8080/readyz

API v1 Endpoints

GET /api/v1/ping

Simple connectivity test

curl http://localhost:8080/api/v1/ping

Response:

{
  "success": true,
  "message": "pong",
  "data": {
    "version": "1.0.0"
  }
}

GET /api/v1/info

API information and metadata

curl http://localhost:8080/api/v1/info

GET /api/v1/exchange/available

List all available exchanges

curl http://localhost:8080/api/v1/exchange/available

Response:

{
  "success": true,
  "data": {
    "exchanges": [
      {
        "id": "binance",
        "name": "binance",
        "country": "JP",
        "url": "https://www.binance.com"
      },
      ...
    ],
    "total": 87
  }
}

GET /api/v1/exchange/symbols

Get trading symbols for a specific exchange

Query Parameters:

  • exchange (required): Exchange name (e.g., "binance", "kraken")
  • type (optional): Filter by type - "spot" or "swap"
  • short (optional): Return minimal data (true/false), default false
# Get all symbols
curl "http://localhost:8080/api/v1/exchange/symbols?exchange=binance"

# Get only spot trading pairs
curl "http://localhost:8080/api/v1/exchange/symbols?exchange=binance&type=spot"

# Get compact symbol list
curl "http://localhost:8080/api/v1/exchange/symbols?exchange=binance&short=true"

Response:

{
  "success": true,
  "data": {
    "exchange": "binance",
    "spot": [
      {
        "id": "BTC/USDT",
        "symbol": "BTC/USDT",
        "base": "BTC",
        "quote": "USDT",
        "type": "spot",
        "active": true
      },
      ...
    ],
    "swap": [...]
  }
}

GET /api/v1/exchange/ohlcv

Get historical OHLCV candle data

Query Parameters:

  • exchange (required): Exchange name
  • symbol (required): Trading pair (e.g., "BTC/USDT")
  • timeframe (required): Candle timeframe ("1m", "5m", "15m", "1h", "4h", "1d", etc.)
  • limit (optional): Number of candles to fetch, default 1000
curl "http://localhost:8080/api/v1/exchange/ohlcv?exchange=binance&symbol=BTC/USDT&timeframe=1h&limit=100"

Configuration

Configuration is managed through environment variables. Create a .env file in the project root:

# Application
APP_NAME=chartwise-api
APP_ENV=development
APP_PORT=8080
APP_VERSION=1.0.0

# Logging
LOG_LEVEL=info              # debug, info, warn, error, fatal
LOG_FORMAT=json             # json or console

# Server
SERVER_READ_TIMEOUT=15s
SERVER_WRITE_TIMEOUT=15s
SERVER_IDLE_TIMEOUT=60s
SERVER_SHUTDOWN_TIMEOUT=10s
SERVER_MAX_HEADER_BYTES=1048576
SERVER_ENABLE_COMPRESSION=true

# CORS
CORS_ALLOWED_ORIGINS=*
CORS_ALLOWED_METHODS=GET,POST,PUT,DELETE,PATCH,OPTIONS
CORS_ALLOWED_HEADERS=Content-Type,Authorization,X-Requested-With
CORS_ALLOW_CREDENTIALS=true
CORS_MAX_AGE=3600

# Rate Limiting
RATE_LIMIT_ENABLED=true
RATE_LIMIT_RPS=100              # Requests per second per IP
RATE_LIMIT_BURST=200            # Burst size

# Caching
CACHE_ENABLED=true
CACHE_TTL=15m                   # Time to live for cached data
CACHE_MAX_SIZE=100
CACHE_MAX_INSTANCES=10

# OHLCV Data Fetching
OHLCV_DEFAULT_LIMIT=1000
OHLCV_RATE_LIMIT_BUFFER=0.5     # Buffer in seconds between requests
OHLCV_MAX_WORKERS=5             # Parallel workers for data fetching

Development Patterns

Adding a New Endpoint

  1. Define DTOs in internal/dto/ for request/response structures:
type MyRequest struct {
    Param string `json:"param" binding:"required"`
}

type MyResponse struct {
    Data string `json:"data"`
}
  1. Create Service in internal/services/ with business logic:
type MyService interface {
    DoSomething(ctx context.Context, req *dto.MyRequest) (*dto.MyResponse, error)
}

type myService struct {
    logger *logger.Logger
    config *config.Config
}

func NewMyService(log *logger.Logger, cfg *config.Config) MyService {
    return &myService{logger: log, config: cfg}
}
  1. Create Handler in internal/handler/ to expose HTTP endpoints:
type MyHandler struct {
    logger  *logger.Logger
    service services.MyService
}

func (h *MyHandler) HandleRequest(c *gin.Context) {
    // Validate input, call service, return response
}

func (h *MyHandler) RegisterRoutes(rg *gin.RouterGroup) {
    rg.POST("/my-endpoint", h.HandleRequest)
}
  1. Register in Router in internal/router/router.go:
myService := services.NewMyService(appLogger, cfg)
myHandler := handler.NewMyHandler(appLogger, myService)
myHandler.RegisterRoutes(v1)

Error Handling

Use centralized error handling from pkg/errors/:

// Return specific HTTP status codes
if err != nil {
    return errors.BadRequest("invalid parameter")
    return errors.NotFound("resource")
    return errors.InternalServer("unexpected error")
    return errors.ServiceUnavailable("external service down")
}

Logging

Use structured logging with key-value pairs:

logger.Info("Operation completed", "user_id", 123, "duration_ms", 45)
logger.Error("Operation failed", "error", err, "retry_count", 3)

Testing

Unit Tests

go test ./...

With Coverage

go test ./... -cover

Benchmarks

go run ./cmd/bench/main.go

Performance Considerations

  • Caching: Exchange symbols are cached with configurable TTL to minimize repeated API calls
  • Parallel Fetching: OHLCV data fetching uses parallel workers to improve throughput
  • Rate Limiting: Configurable per-IP rate limiting prevents service abuse
  • Compression: Automatic response compression reduces bandwidth usage
  • Connection Pooling: HTTP client connection reuse via Gin's built-in pooling

Deployment

Production Checklist

  • Set APP_ENV=production
  • Set LOG_LEVEL=info or warn
  • Enable RATE_LIMIT_ENABLED=true
  • Configure CORS_ALLOWED_ORIGINS (restrict from *)
  • Enable SERVER_ENABLE_COMPRESSION=true
  • Set appropriate CACHE_TTL based on data freshness needs
  • Use external configuration management (e.g., environment variables from secrets)
  • Monitor logs and metrics
  • Set up proper logging aggregation

Troubleshooting

Exchange Not Found Error

Ensure the exchange name is valid and supported by CCXT:

curl http://localhost:8080/api/v1/exchange/available

Rate Limit Errors

Increase RATE_LIMIT_RPS or implement client-side rate limiting.

Cache Not Working

Verify CACHE_ENABLED=true and check TTL with CACHE_TTL setting.

Timeout Issues

Adjust SERVER_READ_TIMEOUT and SERVER_WRITE_TIMEOUT based on network latency.

Dependencies

Key external dependencies:

Contributing

Contributions are welcome! Please follow these guidelines:

  1. Fork the repository
  2. Create a feature branch (git checkout -b feature/my-feature)
  3. Commit changes (git commit -am 'Add my feature')
  4. Push to branch (git push origin feature/my-feature)
  5. Create a Pull Request

License

This project is licensed under the MIT License. See LICENSE file for details.

Support

For issues, questions, or contributions, please open an issue on the GitHub repository.


Built with ❤️ using Go and Gin Framework

About

Vibe coded and learning project with Go language

Topics

Resources

Stars

Watchers

Forks

Languages