# Replenishing Consumption Account Balance

This script helps you keep a minimum buffer of Units available in your Consumption account.&#x20;

* It checks your current balance and, if you are below a threshold, it automatically places a buy order for more Units on the market either as a limit order capped at a max price or as a market order for immediate replenishment.&#x20;
* You can adjust the minimum buffer based on your Consumption needs in every [4 Hour Consumption Window](https://thegrid.ai/docs/introduction/consumption-delivery-and-metering).

{% tabs %}
{% tab title="Python" %}
{% code expandable="true" %}

```py

import base64
import hashlib
import json
import os
import time

import requests
from nacl.signing import SigningKey

BASE_URL = 'https://trading.api.thegrid.ai'
MIN_BALANCE = 5  # Minimum units to maintain
BUY_QUANTITY = 10
MAX_PRICE = 2.50
MARKET_ID = 'market_788dcbd5-ac68-4c61-acf1-4443beaf2a1c'
ORDER_TYPE = os.environ.get('ORDER_TYPE', 'limit').lower()


class SignatureAuth:
    def __init__(self, private_key_b64, public_key_b64):
        self.private_key = SigningKey(base64.b64decode(private_key_b64)[:32])
        public_key_bytes = base64.b64decode(public_key_b64)
        hash_digest = hashlib.sha256(public_key_bytes).digest()
        self.fingerprint = base64.b64encode(hash_digest).decode().rstrip('=')

    def get_headers(self, method, path, body=''):
        timestamp = str(int(time.time()))
        message = f'{timestamp}{method.upper()}{path}{body}'
        signature = self.private_key.sign(message.encode()).signature
        return {
            'x-thegrid-signature': base64.b64encode(signature).decode(),
            'x-thegrid-timestamp': timestamp,
            'x-thegrid-fingerprint': self.fingerprint,
        }


def build_order_payload(order_type):
    order_type = (order_type or 'limit').lower()
    base = {
        'market_id': MARKET_ID,
        'side': 'buy',
        'type': order_type,
        'quantity': BUY_QUANTITY,
        'time_in_force': 'gtc',
        'client_order_id': f'replenish-{int(time.time())}',
    }

    if order_type == 'limit':
        base['price'] = str(MAX_PRICE)
        return base

    if order_type == 'market':
        return base

    raise ValueError('ORDER_TYPE must be "limit" or "market"')


auth = SignatureAuth(
    os.environ['GRID_TRADING_PRIVATE_KEY'],
    os.environ['GRID_TRADING_PUBLIC_KEY'],
)

# Check current consumption balance
balance_path = '/api/v1/trading/consumption-accounts'
balance_resp = requests.get(f'{BASE_URL}{balance_path}?order_by=created_at', headers=auth.get_headers('GET', balance_path))
balance_resp.raise_for_status()

accounts = balance_resp.json()['data']
current_balance = accounts[0]['available_balance'] if accounts else 0

print(f'Current balance: {current_balance} units')

if current_balance >= MIN_BALANCE:
    print(f'Balance is sufficient (>= {MIN_BALANCE}), no replenishment needed')
else:
    print(f'Balance is low (< {MIN_BALANCE}), placing {ORDER_TYPE} buy order...')

    order_data = build_order_payload(ORDER_TYPE)

    order_path = '/api/v1/trading/orders'
    body = json.dumps(order_data)
    headers = auth.get_headers('POST', order_path, body)
    headers['Content-Type'] = 'application/json'

    order_resp = requests.post(f'{BASE_URL}{order_path}', data=body, headers=headers)
    order_resp.raise_for_status()

    print(f"Order placed ({ORDER_TYPE}): {order_resp.json()['data']['order_id']}")

```

{% endcode %}
{% endtab %}

{% tab title="JavaScript" %}

```javascript
import axios from 'axios';
import nacl from 'tweetnacl';
import util from 'tweetnacl-util';

function decodePrivateKey(privateKeyBase64) {
  const raw = util.decodeBase64(privateKeyBase64);
  if (raw.length === nacl.sign.seedLength) {
    return nacl.sign.keyPair.fromSeed(raw).secretKey;
  }
  if (raw.length === nacl.sign.secretKeyLength) {
    return raw;
  }
  throw new Error('Invalid private key length; expected 32-byte seed or 64-byte secret key');
}

const BASE_URL = 'https://trading.api.thegrid.ai';
const MIN_BALANCE = 5;  // Minimum units to maintain
const BUY_QUANTITY = 10;
const MAX_PRICE = 2.50;
const MARKET_ID = 'market_788dcbd5-ac68-4c61-acf1-4443beaf2a1c';
const ORDER_TYPE = (process.env.ORDER_TYPE || 'limit').toLowerCase();

class SignatureAuth {
  constructor(privateKeyBase64, fingerprint) {
    this.privateKey = decodePrivateKey(privateKeyBase64);
    this.fingerprint = fingerprint;
  }

  getHeaders(method, path, body = '') {
    const timestamp = Math.floor(Date.now() / 1000).toString();
    const message = `${timestamp}${method.toUpperCase()}${path}${body}`;
    const messageBytes = util.decodeUTF8(message);
    const signatureBytes = nacl.sign.detached(messageBytes, this.privateKey);
    return {
      'x-thegrid-signature': util.encodeBase64(signatureBytes),
      'x-thegrid-timestamp': timestamp,
      'x-thegrid-fingerprint': this.fingerprint
    };
  }
}

const auth = new SignatureAuth(
  process.env.GRID_TRADING_PRIVATE_KEY,
  process.env.GRID_TRADING_FINGERPRINT
);

function buildOrderPayload(orderType) {
  const type = (orderType || 'limit').toLowerCase();
  if (type === 'limit') {
    return {
      market_id: MARKET_ID,
      side: 'buy',
      type: 'limit',
      quantity: BUY_QUANTITY,
      price: MAX_PRICE.toString(),
      time_in_force: 'gtc',
      client_order_id: `replenish-${Date.now()}`
    };
  }

  if (type === 'market') {
    return {
      market_id: MARKET_ID,
      side: 'buy',
      type: 'market',
      quantity: BUY_QUANTITY,
      time_in_force: 'gtc',
      client_order_id: `replenish-${Date.now()}`
    };
  }

  throw new Error('ORDER_TYPE must be "limit" or "market"');
}

async function submitOrder(orderType) {
  const orderPath = '/api/v1/trading/orders';
  const orderData = buildOrderPayload(orderType);
  const body = JSON.stringify(orderData);

  const orderResp = await axios.post(`${BASE_URL}${orderPath}`, body, {
    headers: {
      'Content-Type': 'application/json',
      ...auth.getHeaders('POST', orderPath, body)
    }
  });

  return orderResp.data.data.order_id;
}

// Check current consumption balance
const balancePath = '/api/v1/trading/consumption-accounts';
const balanceResp = await axios.get(`${BASE_URL}${balancePath}?order_by=created_at`, {
  headers: auth.getHeaders('GET', balancePath)
});

const account = balanceResp.data.data[0];
const currentBalance = account?.available_balance || 0;

console.log(`Current balance: ${currentBalance} units`);

if (currentBalance >= MIN_BALANCE) {
  console.log(`Balance is sufficient (>= ${MIN_BALANCE}), no replenishment needed`);
} else {
  console.log(`Balance is low (< ${MIN_BALANCE}), placing ${ORDER_TYPE} buy order...`);

  const orderId = await submitOrder(ORDER_TYPE);
  console.log(`Order placed (${ORDER_TYPE}): ${orderId}`);
}

```

{% endtab %}

{% tab title="Go" %}

```go

package main

import (
	"bytes"
	"crypto/ed25519"
	"crypto/sha256"
	"encoding/base64"
	"encoding/json"
	"fmt"
	"log"
	"net/http"
	"os"
	"strconv"
	"strings"
	"time"
)

const (
	baseURL     = "https://trading.api.thegrid.ai"
	minBalance  = 5
	buyQuantity = 10
	maxPrice    = 2.50
	marketID    = "market_788dcbd5-ac68-4c61-acf1-4443beaf2a1c"
)

type SignatureAuth struct {
	privateKey  ed25519.PrivateKey
	fingerprint string
}

func buildOrderPayload(orderType string) (map[string]any, error) {
	orderType = strings.ToLower(orderType)
	if orderType == "" {
		orderType = "limit"
	}

	orderData := map[string]any{
		"market_id":       marketID,
		"side":            "buy",
		"type":            orderType,
		"quantity":        buyQuantity,
		"time_in_force":   "gtc",
		"client_order_id": fmt.Sprintf("replenish-%d", time.Now().Unix()),
	}

	switch orderType {
	case "limit":
		orderData["price"] = fmt.Sprintf("%.2f", maxPrice)
	case "market":
		// Market orders execute at the best available price, no price required.
	default:
		return nil, fmt.Errorf("unsupported ORDER_TYPE %q (use limit or market)", orderType)
	}

	return orderData, nil
}

func NewSignatureAuth(privateKeyB64, publicKeyB64 string) *SignatureAuth {
	privateKeyBytes, _ := base64.StdEncoding.DecodeString(privateKeyB64)
	var privateKey ed25519.PrivateKey
	if len(privateKeyBytes) == ed25519.SeedSize {
		privateKey = ed25519.NewKeyFromSeed(privateKeyBytes)
	} else {
		privateKey = ed25519.PrivateKey(privateKeyBytes)
	}

	publicKeyBytes, _ := base64.StdEncoding.DecodeString(publicKeyB64)
	hash := sha256.Sum256(publicKeyBytes)
	fingerprint := strings.TrimRight(base64.StdEncoding.EncodeToString(hash[:]), "=")

	return &SignatureAuth{privateKey: privateKey, fingerprint: fingerprint}
}

func (sa *SignatureAuth) GetHeaders(method, path, body string) map[string]string {
	timestamp := strconv.FormatInt(time.Now().Unix(), 10)
	message := timestamp + method + path + body
	signature := ed25519.Sign(sa.privateKey, []byte(message))
	return map[string]string{
		"x-thegrid-signature":   base64.StdEncoding.EncodeToString(signature),
		"x-thegrid-timestamp":   timestamp,
		"x-thegrid-fingerprint": sa.fingerprint,
	}
}

func main() {
	auth := NewSignatureAuth(
		os.Getenv("GRID_TRADING_PRIVATE_KEY"),
		os.Getenv("GRID_TRADING_PUBLIC_KEY"),
	)

	// Check current consumption balance
	balancePath := "/api/v1/trading/consumption-accounts"
	req, err := http.NewRequest("GET", baseURL+balancePath+"?order_by=created_at", nil)
	if err != nil {
		log.Fatal(err)
	}
	for k, v := range auth.GetHeaders("GET", balancePath, "") {
		req.Header.Set(k, v)
	}

	resp, err := http.DefaultClient.Do(req)
	if err != nil {
		log.Fatal(err)
	}
	defer resp.Body.Close()

	var balanceResult struct {
		Data []struct {
			AvailableBalance float64 `json:"available_balance"`
		} `json:"data"`
	}
	if err := json.NewDecoder(resp.Body).Decode(&balanceResult); err != nil {
		log.Fatal(err)
	}

	currentBalance := 0.0
	if len(balanceResult.Data) > 0 {
		currentBalance = balanceResult.Data[0].AvailableBalance
	}

	fmt.Printf("Current balance: %.0f units\n", currentBalance)

	if currentBalance >= minBalance {
		fmt.Printf("Balance is sufficient (>= %d), no replenishment needed\n", minBalance)
		return
	}

	fmt.Printf("Balance is low (< %d), placing buy order...\n", minBalance)

	orderType := strings.ToLower(os.Getenv("ORDER_TYPE"))
	orderData, err := buildOrderPayload(orderType)
	if err != nil {
		log.Fatal(err)
	}

	orderJSON, err := json.Marshal(orderData)
	if err != nil {
		log.Fatal(err)
	}

	orderPath := "/api/v1/trading/orders"
	orderReq, err := http.NewRequest("POST", baseURL+orderPath, bytes.NewBuffer(orderJSON))
	if err != nil {
		log.Fatal(err)
	}
	orderReq.Header.Set("Content-Type", "application/json")
	for k, v := range auth.GetHeaders("POST", orderPath, string(orderJSON)) {
		orderReq.Header.Set(k, v)
	}

	orderResp, err := http.DefaultClient.Do(orderReq)
	if err != nil {
		log.Fatal(err)
	}
	defer orderResp.Body.Close()

	var orderResult struct {
		Data struct {
			OrderID string `json:"order_id"`
		} `json:"data"`
	}
	if err := json.NewDecoder(orderResp.Body).Decode(&orderResult); err != nil {
		log.Fatal(err)
	}

	fmt.Printf("Order placed (%s): %s\n", orderData["type"], orderResult.Data.OrderID)
}

```

{% endtab %}
{% endtabs %}


---

# Agent Instructions: Querying This Documentation

If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter:

```
GET https://thegrid.ai/docs/technical-guides/replenishing-consumption-account-balance.md?ask=<question>
```

The question should be specific, self-contained, and written in natural language.
The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
