Spot Trading Guide
This guide takes you through the core logic of BigONE's Spot Trading API. Instead of providing complex wrapper classes, we focus on the raw HTTP interactions, JSON payloads, and business logic you need to understand to build a robust trading bot.
Prerequisites
- Base URL:
https://api.big.one/api/v3 - Authentication: Most "Viewer" endpoints (Account, Order) require a Bearer Token (JWT). See the Authentication Guide for details.
- Required Scopes:
view account balance information(to check funds)create orders(to place and cancel orders)view order information(to query status)
- Content-Type: All POST requests must have
Content-Type: application/json.
Lesson 1: Market Rules & Precision
Before placing any order, you must understand the trading rules for your target asset pair. Unlike standard financial markets, crypto markets enforce strict decimal precision limits and minimum order values.
Understanding Asset Pair Configuration
The GET /asset_pairs endpoint provides the rules of engagement. You should call this once at application startup and cache the result.
Key Configuration Fields:
- base_scale: Controls decimal precision for Amount (Quantity).
- Example:
BTC-USDThasbase_scale: 6. Amount0.123456is valid;0.1234567is invalid.
- Example:
- quote_scale: Controls decimal precision for Price.
- Example:
BTC-USDThasquote_scale: 2. Price50000.12is valid;50000.123is invalid.
- Example:
- min_quote_value: The minimum order value (Price × Amount) required.
- Example: If
min_quote_valueis5.0, a buy order for $4.99 will be rejected with400 Bad Request.
- Example: If
Logic Implementation
- Fetch configurations.
- Identify your target pair's scales.
- Validate: Check if
Price * Amount >= min_quote_value. - Format: Convert numbers to strings with exact precision.
import requests
BASE_URL = "https://api.big.one/api/v3"
# 1. Get Configuration
response = requests.get(f"{BASE_URL}/asset_pairs")
pairs = response.json()
pair = next(p for p in pairs if p["name"] == "BTC-USDT")
# 2. Logic: Validation & Formatting
def prepare_order(amount, price, pair_config):
# Check Minimum Value
value = amount * price
min_val = float(pair_config['min_quote_value'])
if value < min_val:
raise ValueError(f"Order value {value} is below minimum {min_val}")
# Format to fixed strings
safe_amount = f"{amount:.{pair_config['base_scale']}f}"
safe_price = f"{price:.{pair_config['quote_scale']}f}"
return safe_amount, safe_price
# Example Usage
try:
amt, prc = prepare_order(0.0001, 50000, pair) # Value = 5.0
print(f"Payload: amount='{amt}', price='{prc}'")
except ValueError as e:
print(f"Validation Error: {e}")
Lesson 2: Funds & Account State
Before trading, verify you have enough Available Balance.
Endpoint: GET /viewer/accounts
Key Response Fields:
- balance: The amount available for new trades or withdrawals.
- locked_balance: The amount currently tied up in open orders.
State Transition:
- You have
100 USDTavailable. - You place a limit buy order for
50 USDT. - New State:
balance = 50,locked_balance = 50. - If the order fills,
locked_balancebecomes0, and you gain BTC. - If you cancel,
locked_balancereturns tobalance.
# Check funds before ordering
accounts = requests.get(f"{BASE_URL}/viewer/accounts", headers=headers).json()
usdt_wallet = next((a for a in accounts if a['asset_symbol'] == 'USDT'), None)
if usdt_wallet and float(usdt_wallet['balance']) > 50.0:
print("Sufficient funds.")
else:
print("Insufficient funds.")
Lesson 3: Placing Orders (The Details)
The POST /viewer/orders endpoint is your primary tool. Let's look at the advanced parameters that control how your order executes.
Order Types (type)
- LIMIT: "I want to buy at price X or better." (Standard)
- MARKET: "I want to buy NOW at any price." (Taker)
- STOP_LIMIT: "If price hits trigger, place a LIMIT order."
- STOP_MARKET: "If price hits trigger, place a MARKET order."
Execution Flags (Time In Force)
These flags are critical for algorithmic trading (e.g., arbitrage) to prevent partial fills or stuck orders.
- immediate_or_cancel (IOC):
- Logic: "Fill what you can right now, and cancel the rest."
- Use Case: You see 10 BTC for sale. You want to buy them, but if someone else buys 5 BTC first, you don't want your remaining buy order sitting in the book.
- post_only:
- Logic: "I must be the Maker. If I cross the spread (act as Taker), cancel me."
- Use Case: Market Making bots that want to earn rebates and avoid taker fees.
Example: The "Safe" Market Maker Order
This order tries to buy at $50,000, but guarantees it won't accidentally pay taker fees.
payload = {
"asset_pair_name": "BTC-USDT",
"side": "BID",
"type": "LIMIT",
"price": "50000.00",
"amount": "0.500000",
"post_only": True, # Crucial for Market Makers
"client_order_id": "mm-bot-001"
}
response = requests.post(f"{BASE_URL}/viewer/orders", json=payload, headers=headers)
# If price is already 49900, this API call will return a cancelled order or error.
Lesson 4: Managing the Lifecycle
Once placed, an order is a living object. You interact with it using its ID.
1. Check Status
Endpoint: GET /viewer/orders/{id}
Status values to handle:
PENDING: Still waiting.FILLED: Done. Checkavg_deal_priceto see the actual trade price.CANCELLED: Stopped by user or system.
2. Cancel Order
Endpoint: POST /viewer/orders/{id}/cancel
Always use the POST method.
3. Batch Cancel (Reset)
Endpoint: POST /viewer/orders/cancel
Useful when your bot restarts or detects a market anomaly.
# Panic Button: Cancel everything
requests.post(f"{BASE_URL}/viewer/orders/cancel",
json={"asset_pair_name": "BTC-USDT"},
headers=headers)
Lesson 5: Error Handling
Don't just check for HTTP 200. BigONE returns useful error codes in the JSON body.
Common Error Codes:
| Code | Message | Meaning | Action |
|---|---|---|---|
10014 | Insufficient funds | Available balance too low. | Check locked_balance or deposit. |
40304 | Order forbidden | Market might be in maintenance. | Stop bot and alert human. |
54041 | Duplicate order | client_order_id reused. | Generate new ID. |
50047 | Liquidity taken too much | post_only order would execute as taker. | Adjust price and retry. |
response = requests.post(f"{BASE_URL}/viewer/orders", json=payload, headers=headers)
if response.status_code != 200:
error = response.json()
code = error.get('code')
if code == 10014:
print("Not enough cash!")
elif code == 50047:
print("Price crossed spread, retrying...")
else:
print(f"Critical API Error: {error['message']}")
Complete Example
Here is a runnable script that combines all the lessons above. It implements a safe "Maker" buy order with status monitoring.
import requests
import time
import jwt # pip install PyJWT
# --- Configuration ---
API_KEY = "YOUR_ACCESS_KEY"
SECRET_KEY = "YOUR_SECRET_KEY"
BASE_URL = "https://api.big.one/api/v3"
PAIR = "BTC-USDT"
# --- Authentication Helper ---
def get_headers():
token = jwt.encode({
"sub": API_KEY,
"nonce": int(time.time() * 1000000), # Microsecond nonce
"iat": int(time.time()),
"exp": int(time.time()) + 60
}, SECRET_KEY, algorithm="HS256")
return {"Authorization": f"Bearer {token}", "Content-Type": "application/json"}
def run_bot():
print(f"--- Starting Spot Bot for {PAIR} ---")
# 1. Get Precision Rules
print("1. Fetching Market Rules...")
pairs = requests.get(f"{BASE_URL}/asset_pairs").json()
pair_cfg = next(p for p in pairs if p["name"] == PAIR)
base_scale = pair_cfg['base_scale'] # Amount precision
quote_scale = pair_cfg['quote_scale'] # Price precision
min_val = float(pair_cfg['min_quote_value'])
# 2. Define Targets
target_price = 40000.50 # Example Price
target_amount = 0.002 # Example Amount
# 3. Validate & Format
order_val = target_price * target_amount
if order_val < min_val:
print(f"Error: Order value {order_val} < Minimum {min_val}")
return
# FORMATTING AS STRINGS IS CRITICAL FOR SPOT API
safe_price = f"{target_price:.{quote_scale}f}"
safe_amount = f"{target_amount:.{base_scale}f}"
print(f" Target: Buy {safe_amount} @ {safe_price} (Val: {order_val})")
# 4. Check Balance
print("2. Checking Funds...")
accounts = requests.get(f"{BASE_URL}/viewer/accounts", headers=get_headers()).json()
# Find Quote Asset (USDT)
quote_asset = pair_cfg['quote_asset']['symbol']
wallet = next((a for a in accounts if a['asset_symbol'] == quote_asset), None)
available = float(wallet['balance']) if wallet else 0.0
if available < order_val:
print(f" Insufficient funds: Have {available}, Need {order_val}")
return
# 5. Place Order (Post-Only)
print("3. Placing Order...")
payload = {
"asset_pair_name": PAIR,
"side": "BID",
"type": "LIMIT",
"price": safe_price, # String
"amount": safe_amount, # String
"post_only": True,
"client_order_id": f"bot-{int(time.time())}"
}
res = requests.post(f"{BASE_URL}/viewer/orders", json=payload, headers=get_headers())
if res.status_code != 200:
print(" Order Failed:", res.json())
return
order_id = res.json()['id']
print(f" Order {order_id} Placed. State: {res.json()['state']}")
# 6. Monitor Status
print("4. Monitoring (5s)...")
time.sleep(5)
status = requests.get(f"{BASE_URL}/viewer/orders/{order_id}", headers=get_headers()).json()
print(f" Current State: {status['state']}, Filled: {status['filled_amount']}")
# 7. Cancel if not filled
if status['state'] == "PENDING":
print(" Order still pending. Cancelling...")
cancel = requests.post(f"{BASE_URL}/viewer/orders/{order_id}/cancel", headers=get_headers())
print(" Result:", cancel.json()['state'])
if __name__ == "__main__":
run_bot()
Conclusion
You now have the tools to build a robust Spot trading application on BigONE. By respecting market precision rules and handling the order lifecycle correctly, you can avoid the most common integration pitfalls.
Key Takeaways:
- Always fetch and cache
asset_pairconfigurations to handle precision (base_scale,quote_scale). - Send numeric values as Strings to prevent floating-point errors.
- Use
post_onlyfor market-making strategies to ensure you remain a liquidity provider.
Next Steps:
- Explore the WebSocket API to receive real-time order updates instead of polling.
- Review the API Reference for the full list of order types and error codes.