今天要學習如何設置和使用 Binance 測試網路 API,這就像爸爸讓我在虛擬的農產品交易所練習買賣一樣。在真正用錢交易之前,先用測試環境熟悉所有流程,確保我們的系統運作正常!
特徵 | Testnet | Mainnet |
---|---|---|
資金 | 虛擬測試幣 | 真實資金 |
交易 | 模擬交易 | 實際交易 |
風險 | 零風險 | 真實風險 |
功能 | 完整 API 功能 | 完整 API 功能 |
限制 | 測試用途 | 生產環境 |
成本 | 免費 | 手續費 |
import requests
import hmac
import hashlib
import time
from urllib.parse import urlencode
import json
class BinanceTestnetClient:
"""Binance 測試網客戶端"""
def __init__(self, api_key: str, secret_key: str):
self.api_key = api_key
self.secret_key = secret_key
self.base_url = "https://testnet.binance.vision"
self.headers = {
'X-MBX-APIKEY': api_key,
'Content-Type': 'application/json'
}
def _generate_signature(self, params: dict) -> str:
"""生成 API 簽名"""
query_string = urlencode(params)
signature = hmac.new(
self.secret_key.encode('utf-8'),
query_string.encode('utf-8'),
hashlib.sha256
).hexdigest()
return signature
def _make_request(self, method: str, endpoint: str, params: dict = None, signed: bool = False):
"""發送 API 請求"""
if params is None:
params = {}
if signed:
params['timestamp'] = int(time.time() * 1000)
params['signature'] = self._generate_signature(params)
url = f"{self.base_url}{endpoint}"
try:
if method == 'GET':
response = requests.get(url, params=params, headers=self.headers)
elif method == 'POST':
response = requests.post(url, params=params, headers=self.headers)
elif method == 'DELETE':
response = requests.delete(url, params=params, headers=self.headers)
response.raise_for_status()
return response.json()
except requests.exceptions.RequestException as e:
print(f"API 請求錯誤: {e}")
if hasattr(e, 'response') and e.response is not None:
print(f"錯誤詳情: {e.response.text}")
return None
def get_server_time(self):
"""獲取服務器時間"""
return self._make_request('GET', '/api/v3/time')
def get_account_info(self):
"""獲取帳戶資訊"""
return self._make_request('GET', '/api/v3/account', signed=True)
def get_symbol_info(self, symbol: str = None):
"""獲取交易對資訊"""
endpoint = '/api/v3/exchangeInfo'
params = {}
if symbol:
params['symbol'] = symbol
return self._make_request('GET', endpoint, params)
def get_ticker_price(self, symbol: str):
"""獲取價格資訊"""
params = {'symbol': symbol}
return self._make_request('GET', '/api/v3/ticker/price', params)
def get_order_book(self, symbol: str, limit: int = 100):
"""獲取訂單簿"""
params = {
'symbol': symbol,
'limit': limit
}
return self._make_request('GET', '/api/v3/depth', params)
def get_klines(self, symbol: str, interval: str, limit: int = 500):
"""獲取 K 線數據"""
params = {
'symbol': symbol,
'interval': interval,
'limit': limit
}
return self._make_request('GET', '/api/v3/klines', params)
def place_order(self, symbol: str, side: str, order_type: str,
quantity: float, price: float = None, **kwargs):
"""下單"""
params = {
'symbol': symbol,
'side': side.upper(),
'type': order_type.upper(),
'quantity': quantity
}
if price and order_type.upper() == 'LIMIT':
params['price'] = price
params['timeInForce'] = kwargs.get('timeInForce', 'GTC')
# 添加其他參數
for key, value in kwargs.items():
if key not in params:
params[key] = value
return self._make_request('POST', '/api/v3/order', params, signed=True)
def get_open_orders(self, symbol: str = None):
"""獲取未成交訂單"""
params = {}
if symbol:
params['symbol'] = symbol
return self._make_request('GET', '/api/v3/openOrders', params, signed=True)
def cancel_order(self, symbol: str, order_id: int):
"""取消訂單"""
params = {
'symbol': symbol,
'orderId': order_id
}
return self._make_request('DELETE', '/api/v3/order', params, signed=True)
def get_order_history(self, symbol: str, limit: int = 500):
"""獲取歷史訂單"""
params = {
'symbol': symbol,
'limit': limit
}
return self._make_request('GET', '/api/v3/allOrders', params, signed=True)
# API 金鑰設置範例
def setup_testnet_credentials():
"""設置測試網憑證"""
# 注意:這些是範例值,請使用您自己的測試網 API 金鑰
API_KEY = "your_testnet_api_key_here"
SECRET_KEY = "your_testnet_secret_key_here"
# 驗證憑證格式
if len(API_KEY) < 64 or len(SECRET_KEY) < 64:
print("警告: API 金鑰長度不正確,請檢查設置")
return None, None
return API_KEY, SECRET_KEY
# 測試連接
def test_api_connection():
"""測試 API 連接"""
# 獲取憑證
api_key, secret_key = setup_testnet_credentials()
if not api_key or not secret_key:
print("請先設置正確的 API 憑證")
return False
# 創建客戶端
client = BinanceTestnetClient(api_key, secret_key)
print("測試 Binance Testnet 連接...")
# 測試 1: 獲取服務器時間
print("\n1. 測試服務器時間:")
server_time = client.get_server_time()
if server_time:
print(f" ✅ 服務器時間: {server_time}")
else:
print(" ❌ 無法獲取服務器時間")
return False
# 測試 2: 獲取帳戶資訊
print("\n2. 測試帳戶資訊:")
account_info = client.get_account_info()
if account_info:
print(f" ✅ 帳戶類型: {account_info.get('accountType', 'Unknown')}")
print(f" ✅ 可交易: {account_info.get('canTrade', False)}")
# 顯示餘額
balances = account_info.get('balances', [])
non_zero_balances = [b for b in balances if float(b['free']) > 0 or float(b['locked']) > 0]
if non_zero_balances:
print(" 💰 帳戶餘額:")
for balance in non_zero_balances[:5]: # 只顯示前5個
print(f" {balance['asset']}: {balance['free']} (可用) + {balance['locked']} (凍結)")
else:
print(" ⚠️ 帳戶餘額為空,請到測試網獲取測試幣")
else:
print(" ❌ 無法獲取帳戶資訊")
return False
# 測試 3: 獲取市場數據
print("\n3. 測試市場數據:")
btc_price = client.get_ticker_price('BTCUSDT')
if btc_price:
print(f" ✅ BTC 價格: ${float(btc_price['price']):,.2f}")
else:
print(" ❌ 無法獲取價格數據")
# 測試 4: 獲取 K 線數據
print("\n4. 測試 K 線數據:")
klines = client.get_klines('BTCUSDT', '1h', 5)
if klines and len(klines) > 0:
print(f" ✅ 獲取到 {len(klines)} 筆 K 線數據")
latest_kline = klines[-1]
print(f" 📊 最新 K 線: 開盤 ${float(latest_kline[1]):,.2f}, 收盤 ${float(latest_kline[4]):,.2f}")
else:
print(" ❌ 無法獲取 K 線數據")
print("\n✅ API 連接測試完成!")
return True
# 運行測試
test_result = test_api_connection()
class TestnetFaucet:
"""測試網資金獲取器"""
def __init__(self):
self.faucet_url = "https://testnet.binance.vision"
def request_test_funds(self, api_key: str):
"""請求測試資金"""
print("🚰 請求測試網資金...")
print(f"📋 步驟說明:")
print(f"1. 訪問 {self.faucet_url}")
print(f"2. 使用您的測試網 API Key 登入")
print(f"3. 點擊 'Get Test Funds' 按鈕")
print(f"4. 系統會自動分配測試幣到您的帳戶")
print(f"\n💡 常見測試幣種:")
print(f" • BTC (Bitcoin)")
print(f" • ETH (Ethereum)")
print(f" • BNB (Binance Coin)")
print(f" • USDT (Tether)")
print(f" • BUSD (Binance USD)")
print(f"\n⚠️ 注意事項:")
print(f" • 測試幣無真實價值")
print(f" • 每個帳戶有申請限制")
print(f" • 測試幣會定期重置")
def check_balance_adequacy(self, client, required_balances: dict):
"""檢查餘額是否足夠測試"""
account_info = client.get_account_info()
if not account_info:
return False, "無法獲取帳戶資訊"
balances = {b['asset']: float(b['free']) for b in account_info['balances']}
insufficient_assets = []
for asset, required_amount in required_balances.items():
current_balance = balances.get(asset, 0)
if current_balance < required_amount:
insufficient_assets.append({
'asset': asset,
'required': required_amount,
'current': current_balance,
'shortage': required_amount - current_balance
})
if insufficient_assets:
return False, insufficient_assets
else:
return True, "餘額充足"
def setup_testnet_environment():
"""設置完整的測試網環境"""
print("🚀 設置 Binance Testnet 環境")
print("=" * 50)
# 步驟 1: API 金鑰設置
print("\n📝 步驟 1: API 金鑰設置")
print("1. 訪問 https://testnet.binance.vision/")
print("2. 使用 GitHub 帳戶登入")
print("3. 創建新的 API Key")
print("4. 記錄 API Key 和 Secret Key")
print("5. 設置 IP 白名單(可選)")
# 步驟 2: 環境變數設置
print("\n🔧 步驟 2: 環境變數設置")
env_template = """
# .env 檔案範例
BINANCE_TESTNET_API_KEY=your_api_key_here
BINANCE_TESTNET_SECRET_KEY=your_secret_key_here
# 可選設置
TESTNET_BASE_URL=https://testnet.binance.vision
TRADING_SYMBOL=BTCUSDT
DEFAULT_QUANTITY=0.001
"""
print(env_template)
# 步驟 3: 安全性設置
print("\n🔒 步驟 3: 安全性設置")
security_checklist = [
"✓ 永遠不要在程式碼中硬編碼 API 金鑰",
"✓ 使用環境變數或配置檔案存儲憑證",
"✓ 設置 IP 白名單限制訪問",
"✓ 定期輪換 API 金鑰",
"✓ 監控 API 使用情況",
"✓ 測試網金鑰與正式網金鑰分開管理"
]
for item in security_checklist:
print(f" {item}")
# 步驟 4: 測試資金申請
print("\n💰 步驟 4: 測試資金申請")
faucet = TestnetFaucet()
# 假設已有 API 金鑰
api_key = "example_api_key"
faucet.request_test_funds(api_key)
print("\n✅ 環境設置完成!")
# 執行環境設置
setup_testnet_environment()
class TradingFunctionTester:
"""交易功能測試器"""
def __init__(self, client: BinanceTestnetClient):
self.client = client
self.test_symbol = 'BTCUSDT'
self.test_results = []
def test_market_data_functions(self):
"""測試市場數據功能"""
print("📊 測試市場數據功能...")
tests = [
{
'name': '獲取交易對資訊',
'function': lambda: self.client.get_symbol_info(self.test_symbol),
'validator': lambda x: x and 'symbols' in x
},
{
'name': '獲取當前價格',
'function': lambda: self.client.get_ticker_price(self.test_symbol),
'validator': lambda x: x and 'price' in x
},
{
'name': '獲取訂單簿',
'function': lambda: self.client.get_order_book(self.test_symbol, 10),
'validator': lambda x: x and 'bids' in x and 'asks' in x
},
{
'name': '獲取 K 線數據',
'function': lambda: self.client.get_klines(self.test_symbol, '1m', 10),
'validator': lambda x: x and len(x) > 0
}
]
for test in tests:
try:
result = test['function']()
if test['validator'](result):
print(f" ✅ {test['name']}: 成功")
self.test_results.append({'test': test['name'], 'status': 'pass'})
else:
print(f" ❌ {test['name']}: 驗證失敗")
self.test_results.append({'test': test['name'], 'status': 'fail'})
except Exception as e:
print(f" ❌ {test['name']}: 錯誤 - {e}")
self.test_results.append({'test': test['name'], 'status': 'error', 'error': str(e)})
def test_account_functions(self):
"""測試帳戶功能"""
print("\n👤 測試帳戶功能...")
# 獲取帳戶資訊
try:
account_info = self.client.get_account_info()
if account_info:
print(" ✅ 獲取帳戶資訊: 成功")
# 檢查餘額
balances = account_info.get('balances', [])
usdt_balance = next((b for b in balances if b['asset'] == 'USDT'), None)
btc_balance = next((b for b in balances if b['asset'] == 'BTC'), None)
if usdt_balance:
print(f" 💵 USDT 餘額: {usdt_balance['free']}")
if btc_balance:
print(f" ₿ BTC 餘額: {btc_balance['free']}")
self.test_results.append({'test': '獲取帳戶資訊', 'status': 'pass'})
else:
print(" ❌ 獲取帳戶資訊: 失敗")
self.test_results.append({'test': '獲取帳戶資訊', 'status': 'fail'})
except Exception as e:
print(f" ❌ 獲取帳戶資訊: 錯誤 - {e}")
self.test_results.append({'test': '獲取帳戶資訊', 'status': 'error', 'error': str(e)})
def test_order_functions(self):
"""測試訂單功能"""
print("\n📋 測試訂單功能...")
# 獲取當前價格
current_price_data = self.client.get_ticker_price(self.test_symbol)
if not current_price_data:
print(" ❌ 無法獲取當前價格,跳過訂單測試")
return
current_price = float(current_price_data['price'])
# 測試限價單 (遠離市價,避免成交)
test_buy_price = current_price * 0.5 # 50% 折扣的買單
test_sell_price = current_price * 1.5 # 150% 溢價的賣單
test_quantity = 0.001 # 小額測試
# 測試下買單
try:
buy_order = self.client.place_order(
symbol=self.test_symbol,
side='BUY',
order_type='LIMIT',
quantity=test_quantity,
price=test_buy_price,
timeInForce='GTC'
)
if buy_order and 'orderId' in buy_order:
print(f" ✅ 下買單成功: 訂單 ID {buy_order['orderId']}")
# 取消訂單
cancel_result = self.client.cancel_order(self.test_symbol, buy_order['orderId'])
if cancel_result:
print(f" ✅ 取消買單成功")
else:
print(f" ⚠️ 取消買單失敗")
self.test_results.append({'test': '限價買單', 'status': 'pass'})
else:
print(f" ❌ 下買單失敗")
self.test_results.append({'test': '限價買單', 'status': 'fail'})
except Exception as e:
print(f" ❌ 限價買單錯誤: {e}")
self.test_results.append({'test': '限價買單', 'status': 'error', 'error': str(e)})
# 獲取歷史訂單
try:
order_history = self.client.get_order_history(self.test_symbol, 10)
if order_history:
print(f" ✅ 獲取訂單歷史: {len(order_history)} 筆記錄")
self.test_results.append({'test': '獲取訂單歷史', 'status': 'pass'})
else:
print(f" ❌ 獲取訂單歷史失敗")
self.test_results.append({'test': '獲取訂單歷史', 'status': 'fail'})
except Exception as e:
print(f" ❌ 獲取訂單歷史錯誤: {e}")
self.test_results.append({'test': '獲取訂單歷史', 'status': 'error', 'error': str(e)})
def run_comprehensive_test(self):
"""執行綜合測試"""
print("🧪 開始 Binance Testnet 綜合功能測試")
print("=" * 60)
self.test_market_data_functions()
self.test_account_functions()
self.test_order_functions()
# 測試結果總結
print(f"\n📈 測試結果總結:")
print("=" * 30)
passed_tests = [r for r in self.test_results if r['status'] == 'pass']
failed_tests = [r for r in self.test_results if r['status'] == 'fail']
error_tests = [r for r in self.test_results if r['status'] == 'error']
print(f"✅ 通過: {len(passed_tests)} 項")
print(f"❌ 失敗: {len(failed_tests)} 項")
print(f"⚠️ 錯誤: {len(error_tests)} 項")
if failed_tests or error_tests:
print(f"\n問題詳情:")
for test in failed_tests + error_tests:
print(f" • {test['test']}: {test['status']}")
if 'error' in test:
print(f" 錯誤: {test['error']}")
success_rate = len(passed_tests) / len(self.test_results) * 100
print(f"\n🎯 成功率: {success_rate:.1f}%")
if success_rate >= 80:
print("🎉 測試網環境設置成功!")
else:
print("⚠️ 部分功能有問題,請檢查設置")
return self.test_results
# 執行綜合測試
def run_testnet_comprehensive_test():
"""運行測試網綜合測試"""
# 這裡需要您的實際 API 憑證
api_key, secret_key = setup_testnet_credentials()
if not api_key or not secret_key:
print("❌ 請先設置正確的 API 憑證")
return
# 創建客戶端和測試器
client = BinanceTestnetClient(api_key, secret_key)
tester = TradingFunctionTester(client)
# 執行測試
results = tester.run_comprehensive_test()
return results
# 如果有正確的憑證,可以運行測試
# test_results = run_testnet_comprehensive_test()
class TestnetTradingBot:
"""測試網交易機器人"""
def __init__(self, client: BinanceTestnetClient):
self.client = client
self.symbol = 'BTCUSDT'
self.position = 0 # 當前持倉
self.trades = [] # 交易記錄
self.running = False
async def simple_momentum_strategy(self):
"""簡單動量策略"""
print("🤖 啟動簡單動量策略測試...")
self.running = True
while self.running:
try:
# 獲取最近的 K 線數據
klines = self.client.get_klines(self.symbol, '5m', 20)
if not klines or len(klines) < 20:
await asyncio.sleep(30)
continue
# 計算簡單移動平均
closes = [float(kline[4]) for kline in klines]
sma_10 = sum(closes[-10:]) / 10
sma_20 = sum(closes[-20:]) / 20
current_price = closes[-1]
print(f"📊 當前價格: ${current_price:.2f}")
print(f"📈 SMA(10): ${sma_10:.2f}, SMA(20): ${sma_20:.2f}")
# 交易信號邏輯
if sma_10 > sma_20 and self.position <= 0:
# 黃金交叉,買入信號
await self._execute_buy_signal(current_price)
elif sma_10 < sma_20 and self.position > 0:
# 死亡交叉,賣出信號
await self._execute_sell_signal(current_price)
# 等待下個週期
await asyncio.sleep(300) # 5分鐘
except Exception as e:
print(f"❌ 策略執行錯誤: {e}")
await asyncio.sleep(60)
async def _execute_buy_signal(self, current_price: float):
"""執行買入信號"""
print("💰 檢測到買入信號...")
# 獲取帳戶餘額
account_info = self.client.get_account_info()
if not account_info:
print("❌ 無法獲取帳戶資訊")
return
# 查找 USDT 餘額
balances = account_info.get('balances', [])
usdt_balance = next((b for b in balances if b['asset'] == 'USDT'), None)
if not usdt_balance or float(usdt_balance['free']) < 50:
print("❌ USDT 餘額不足 (需要至少 $50)")
return
# 計算購買數量
available_usdt = float(usdt_balance['free'])
buy_amount = min(available_usdt * 0.1, 100) # 使用 10% 資金或最多 $100
quantity = round(buy_amount / current_price, 6)
if quantity < 0.001: # 最小交易量檢查
print("❌ 計算出的購買數量太小")
return
# 下市價買單
try:
order = self.client.place_order(
symbol=self.symbol,
side='BUY',
order_type='MARKET',
quantity=quantity
)
if order and 'orderId' in order:
print(f"✅ 買入成功: {quantity} BTC @ ~${current_price:.2f}")
self.position += quantity
trade_record = {
'timestamp': time.time(),
'action': 'BUY',
'quantity': quantity,
'price': current_price,
'order_id': order['orderId']
}
self.trades.append(trade_record)
else:
print(f"❌ 買入失敗: {order}")
except Exception as e:
print(f"❌ 買入執行錯誤: {e}")
async def _execute_sell_signal(self, current_price: float):
"""執行賣出信號"""
print("💸 檢測到賣出信號...")
if self.position <= 0:
print("❌ 無持倉可賣出")
return
# 下市價賣單
try:
quantity = round(self.position, 6)
order = self.client.place_order(
symbol=self.symbol,
side='SELL',
order_type='MARKET',
quantity=quantity
)
if order and 'orderId' in order:
print(f"✅ 賣出成功: {quantity} BTC @ ~${current_price:.2f}")
trade_record = {
'timestamp': time.time(),
'action': 'SELL',
'quantity': quantity,
'price': current_price,
'order_id': order['orderId']
}
self.trades.append(trade_record)
# 計算損益
buy_trades = [t for t in self.trades if t['action'] == 'BUY']
if buy_trades:
avg_buy_price = sum(t['price'] * t['quantity'] for t in buy_trades) / sum(t['quantity'] for t in buy_trades)
pnl = (current_price - avg_buy_price) * quantity
pnl_pct = (current_price / avg_buy_price - 1) * 100
print(f"📊 本次交易損益: ${pnl:.2f} ({pnl_pct:+.2f}%)")
self.position = 0 # 重置持倉
else:
print(f"❌ 賣出失敗: {order}")
except Exception as e:
print(f"❌ 賣出執行錯誤: {e}")
def stop_strategy(self):
"""停止策略"""
print("🛑 停止交易策略...")
self.running = False
def get_performance_summary(self):
"""獲取績效總結"""
if not self.trades:
return "尚無交易記錄"
buy_trades = [t for t in self.trades if t['action'] == 'BUY']
sell_trades = [t for t in self.trades if t['action'] == 'SELL']
total_buy_value = sum(t['price'] * t['quantity'] for t in buy_trades)
total_sell_value = sum(t['price'] * t['quantity'] for t in sell_trades)
realized_pnl = total_sell_value - total_buy_value
summary = {
'total_trades': len(self.trades),
'buy_trades': len(buy_trades),
'sell_trades': len(sell_trades),
'realized_pnl': realized_pnl,
'current_position': self.position,
'trades_detail': self.trades
}
return summary
# 示範如何使用交易機器人
async def demo_trading_bot():
"""演示交易機器人"""
# 需要實際的 API 憑證
api_key, secret_key = setup_testnet_credentials()
if not api_key or not secret_key:
print("請先設置 API 憑證")
return
client = BinanceTestnetClient(api_key, secret_key)
bot = TestnetTradingBot(client)
try:
# 運行策略 (這裡只運行一小段時間作為演示)
print("開始測試網交易機器人演示...")
# 在實際應用中,這會持續運行
# await bot.simple_momentum_strategy()
# 演示版本:模擬幾個交易週期
print("模擬交易策略執行...")
# 獲取當前市場數據
price_data = client.get_ticker_price('BTCUSDT')
if price_data:
current_price = float(price_data['price'])
print(f"當前 BTC 價格: ${current_price:.2f}")
print("✅ 交易機器人演示完成")
except KeyboardInterrupt:
print("\n👋 用戶中斷,停止機器人")
bot.stop_strategy()
# 顯示績效總結
performance = bot.get_performance_summary()
print(f"\n📊 績效總結: {performance}")
# 如果在異步環境中,可以運行:
# await demo_trading_bot()
今天我們學習了如何設置和使用 Binance 測試網路,就像在安全的模擬環境中練習農產品交易。重要概念包括:
測試網設置:
API 功能測試:
自動化測試:
最佳實踐:
記住爸爸說的:「新工具要先在安全的地方練習,熟練了再到真正的市場使用」。測試網是我們學習和驗證策略的最佳場所,絕對不要跳過這個重要步驟!
明天我們將學習如何在 S3 上管理交易配置,為系統的雲端部署做準備。
下一篇:Day 28 - Trading Config Setting on s3