A high-frequency market-neutral spread arbitrage trading strategy for PAXG/USDT and XAUT/USDT perpetual swaps on Bybit, built with the NautilusTrader algorithmic trading framework.
- Overview
- Features
- Strategy Logic
- Architecture
- Installation
- Configuration
- Usage
- Monitoring
- Risk Management
- Performance
- Troubleshooting
- Contributing
- Disclaimer
- License
GoldArb is an automated trading strategy that exploits price spreads between two highly correlated gold-backed tokens:
- PAXG (Pax Gold): ERC-20 token backed by physical gold
- XAUT (Tether Gold): ERC-20 token backed by physical gold
By trading perpetual swap contracts on Bybit, the strategy captures arbitrage opportunities while maintaining a market-neutral hedged position.
- Market Neutral: Long one asset, short the other - no directional exposure
- Grid-Based: Multiple price levels with predefined entry/exit points
- High Frequency: Real-time quote monitoring and rapid order execution
- Risk Controlled: Built-in position limits, notional caps, and extreme spread stops
- Maker Orders: Uses limit orders to earn maker rebates and minimize fees
- ✅ Real-time market data streaming from Bybit WebSocket
- ✅ Automated quote tick subscription for accurate spread calculation
- ✅ Multi-level grid trading with configurable spread thresholds
- ✅ Position reconciliation on startup and periodic snapshots
- ✅ Comprehensive logging (JSON format, DEBUG/INFO levels)
- ✅ Docker containerization for easy deployment
- ✅ Testnet support for risk-free testing
- ✅ Emergency stop mechanisms for extreme market conditions
- ✅ Graceful shutdown with order cancellation and position management
-
Spread Calculation
spread = (PAXG_price - XAUT_price) / XAUT_price -
Grid Entry Logic
- When
spread > grid_level: Open hedged position- SELL PAXG (expensive asset)
- BUY XAUT (cheap asset)
- Each grid level represents a specific spread threshold (e.g., 0.10%, 0.20%, etc.)
- When
-
Grid Exit Logic
- When spread reverts below the grid level: Close position
- Profit is locked in from spread convergence
-
Order Execution
- All orders are limit orders (maker)
- Orders placed with small offset from mid-price for better fill rates
- 5-second timeout for unfilled orders (cancel and resubmit)
1. Initial State:
PAXG = $2,700.00
XAUT = $2,695.00
Spread = 0.185% (above 0.10% grid)
2. Entry:
→ SELL 0.023 PAXG @ $2,700.00
→ BUY 0.023 XAUT @ $2,695.00
→ Position opened at 0.10% grid level
3. Spread Converges:
PAXG = $2,697.00
XAUT = $2,696.00
Spread = 0.037% (below 0.10% grid)
4. Exit:
→ BUY 0.023 PAXG @ $2,697.00 (close short)
→ SELL 0.023 XAUT @ $2,696.00 (close long)
→ Profit: ~$0.046 per unit (minus fees)
- Framework: NautilusTrader v1.221.0
- Exchange: Bybit (Unified Trading Account)
- Language: Python 3.12
- Containerization: Docker with multi-stage builds
- Logging: Structured JSON logging with rotation
GoldArb/
├── paxg_xaut_grid_strategy.py # Core strategy implementation
├── config_live.py # Live trading configuration
├── run_live.py # Main entry point
├── requirements.txt # Python dependencies
├── Dockerfile # Docker image definition
├── .env.example # Environment template
├── .dockerignore # Docker ignore patterns
├── logs/ # Trading logs (auto-created)
│ └── paxg_xaut_grid.json
├── data/ # Historical data (optional)
└── README.md # This file
- CPU: 2+ cores (ARM64 or x86_64)
- RAM: 1GB minimum, 2GB recommended
- Storage: 5GB for Docker images and logs
- Network: Stable internet connection (low latency preferred)
- OS: Linux (Ubuntu 20.04+), macOS, or Windows with WSL2
Docker provides the easiest and most reliable deployment method.
- Docker 20.10+
- Docker Compose 1.29+
- Bybit API credentials
-
Clone the repository
git clone https://github.com/Patrick-code-Bot/GoldArb.git cd GoldArb -
Configure environment variables
cp .env.example .env nano .env # Edit with your API credentialsRequired variables:
BYBIT_API_KEY=your_api_key_here BYBIT_API_SECRET=your_api_secret_here BYBIT_TESTNET=false # Set to 'true' for testnet -
Build and run with Docker Compose (if using orchestration)
cd ../trading-deployment docker-compose up -d goldarbOr run standalone:
docker build -t goldarb . docker run -d \ --name goldarb \ --env-file .env \ -v $(pwd)/logs:/app/logs \ --restart unless-stopped \ goldarb
-
Monitor logs
docker logs -f goldarb
- ✅ Multi-stage builds for optimized image size
- ✅ Non-root user for security
- ✅ Health checks for container monitoring
- ✅ Automatic restarts on failure
- ✅ Volume mounts for persistent logs
For development or testing without Docker:
-
Install Python 3.10+
python3 --version # Should be 3.10 or higher -
Clone and install dependencies
git clone https://github.com/Patrick-code-Bot/GoldArb.git cd GoldArb pip install -r requirements.txt -
Configure environment
cp .env.example .env nano .env # Add your API credentials -
Run the strategy
python run_live.py
Edit config_live.py to customize the trading parameters:
strategy_config = PaxgXautGridConfig(
# Instruments (Bybit LINEAR perpetual swaps)
paxg_instrument_id="PAXGUSDT-LINEAR.BYBIT",
xaut_instrument_id="XAUTUSDT-LINEAR.BYBIT",
# Grid levels (spread as decimal percentage)
grid_levels=[
0.0010, # 0.10% spread
0.0020, # 0.20% spread
0.0030, # 0.30% spread
0.0040, # 0.40% spread
0.0050, # 0.50% spread
0.0060, # 0.60% spread
0.0080, # 0.80% spread
0.0100, # 1.00% spread
],
# Risk management
base_notional_per_level=100.0, # USDT per grid level
max_total_notional=1000.0, # Maximum total exposure (USDT)
target_leverage=10.0, # Target leverage (for reference)
# Trading parameters
maker_offset_bps=2.0, # 0.02% price offset for limit orders
order_timeout_sec=5.0, # Cancel and resubmit after 5s
rebalance_threshold_bps=20.0, # 0.20% imbalance triggers rebalance
extreme_spread_stop=0.015, # 1.5% spread triggers emergency stop
# Features
enable_high_levels=True, # Allow upper grid levels
auto_subscribe=True, # Auto-subscribe to market data
order_id_tag="001", # Unique strategy identifier
)base_notional_per_level=50.0 # $50 per level
max_total_notional=500.0 # $500 max exposure
target_leverage=5.0 # 5x leverageRecommended Capital: $1,000+ USDT
base_notional_per_level=100.0 # $100 per level
max_total_notional=1000.0 # $1,000 max exposure
target_leverage=10.0 # 10x leverageRecommended Capital: $2,000+ USDT
base_notional_per_level=500.0 # $500 per level
max_total_notional=5000.0 # $5,000 max exposure
target_leverage=15.0 # 15x leverageRecommended Capital: $10,000+ USDT
# Using Docker
docker start goldarb
# Using Python
python run_live.pyExpected output:
================================================================================
PAXG-XAUT Grid Strategy - Live Trading
================================================================================
✅ Running in LIVE mode
================================================================================
[1/5] Loading configuration...
[2/5] Building trading node...
[3/5] Registering Bybit adapters...
[4/5] Initializing trading node...
[5/5] Starting trading node...
================================================================================
🚀 Trading node started successfully!
================================================================================
Strategy: PAXG-XAUT Grid Arbitrage
Venue: Bybit (Live)
Instruments:
- PAXGUSDT-LINEAR.BYBIT
- XAUTUSDT-LINEAR.BYBIT
Press Ctrl+C to stop the trading node...
================================================================================
For risk-free testing:
- Create testnet API keys at https://testnet.bybit.com
- Update
.env:BYBIT_TESTNET=true BYBIT_API_KEY=testnet_api_key BYBIT_API_SECRET=testnet_api_secret
- Restart the strategy
Graceful Shutdown:
# Docker
docker stop goldarb
# Python (Press Ctrl+C in terminal)The strategy will:
- Cancel all pending orders
- Log final positions
- Save state snapshots
- Shut down cleanly
Force Stop (not recommended):
docker kill goldarb# Follow live logs
docker logs -f goldarb
# Last 100 lines
docker logs --tail 100 goldarb
# Search for errors
docker logs goldarb 2>&1 | grep ERRORLocation: logs/paxg_xaut_grid.json
Format: Structured JSON with fields:
timestamp: ISO 8601 timestamptrader_id: TRADER-001level: DEBUG, INFO, WARNING, ERRORcomponent: Strategy, ExecEngine, DataClient, etc.message: Log message
Example:
{
"timestamp": "2025-12-15T12:48:45.480095413Z",
"trader_id": "TRADER-001",
"level": "INFO",
"component": "PaxgXautGridStrategy",
"message": "OrderFilled(instrument_id=XAUTUSDT-LINEAR.BYBIT, ...)"
}Monitor these critical metrics:
| Metric | Description | Alert Threshold |
|---|---|---|
| Spread | Current PAXG-XAUT price difference | > 1.5% (extreme) |
| Active Grids | Number of open grid positions | Approaching max |
| Total Notional | Current exposure vs max limit | > 90% of max |
| Order Fill Rate | % of orders filled | < 80% |
| Unrealized PnL | Open position P&L | Large negative |
| API Latency | Bybit API response time | > 500ms |
Monitor positions and orders:
- Live: https://www.bybit.com/trade/usdt/PAXGUSDT
- Testnet: https://testnet.bybit.com/trade/usdt/PAXGUSDT
Check:
- Open positions
- Order history
- Account balance
- Funding rates
- Liquidation price
-
Position Limits
max_total_notional: Hard cap on total exposure- Prevents over-leveraging
-
Extreme Spread Stop
extreme_spread_stop = 0.015(1.5%)- Pauses strategy if spread becomes abnormal
- Prevents trading during market dislocation
-
Order Timeouts
order_timeout_sec = 5.0- Cancels stale orders
- Ensures fresh pricing
-
Position Reconciliation
- On startup: Reconciles local state with exchange
- Periodic snapshots every 5 minutes
- Prevents state drift
-
Maker-Only Orders
- All orders are limit orders
- Earns maker rebates
- Avoids paying taker fees
- Test on testnet for 24+ hours
- Verify API keys have correct permissions
- Set Bybit account leverage (10x recommended)
- Fund account with sufficient margin
- Configure position size alerts on Bybit mobile app
- Document emergency procedures
- Check positions and P&L (morning/evening)
- Review log files for warnings/errors
- Monitor account balance and margin
- Verify strategy is running (Docker health check)
- Check for NautilusTrader updates
If something goes wrong:
-
Stop the Strategy
docker stop goldarb
-
Assess Positions
- Log into Bybit
- Check open positions and orders
- Review account P&L
-
Manual Intervention (if needed)
- Use Bybit web interface to manually close positions
- Cancel any stuck orders
- Document what happened
-
Review Logs
tail -1000 logs/paxg_xaut_grid.json | grep ERROR -
Restart (only after resolving issues)
docker start goldarb
Note: Past performance does not guarantee future results.
- Target: 0.5-2% weekly return (annualized 26-104%)
- Sharpe Ratio: 2-4 (market neutral, low volatility)
- Max Drawdown: < 10% (with proper risk management)
- Win Rate: 75-85% (mean reversion strategy)
Bybit perpetual swaps (as of Dec 2025):
- Maker Fee: -0.01% (rebate)
- Taker Fee: +0.06%
- Funding Rate: ±0.01% per 8 hours (variable)
Strategy uses maker orders only → earning rebates on every fill!
With default configuration (8 grid levels, $100 per level):
Grid Level: 0.10%
Entry Spread: 0.185%
Exit Spread: 0.037%
Profit per Grid: ~$0.046 per unit × 0.023 units = $1.06
Maker Rebate: -0.01% × $200 notional = $0.20
Net P&L: $1.26 per grid close
Daily Average: 5-10 grid closes
Daily P&L: $6.30 - $12.60
Monthly Estimate: $189 - $378 (19-38% ROI on $1000 capital)
Results vary based on market volatility and spread behavior.
Cause: Strategy has reached maximum exposure limit.
Solution:
- This is expected behavior when positions are at max
- Wait for positions to close before new grids open
- Or increase
max_total_notionalin config (higher risk)
Symptoms: Strategy running but no OrderSubmitted logs.
Diagnosis:
# Check if quote data is flowing
docker logs goldarb 2>&1 | grep QuoteTick
# Check spread warnings
docker logs goldarb 2>&1 | grep spreadSolutions:
- Verify instruments are correct:
PAXGUSDT-LINEAR.BYBIT - Check quote tick subscription is active
- Ensure spread is crossing grid levels
- Review
extreme_spread_stopthreshold
Symptoms: OrderRejected events in logs.
Common Causes:
- Insufficient margin/balance
- Incorrect leverage settings
- Position limits exceeded
- Price too far from market (stale)
Solutions:
- Check Bybit account balance
- Verify leverage is set to 10x on Bybit
- Review
maker_offset_bpsin config - Check API rate limits
Diagnosis:
docker ps -a | grep goldarb
docker logs goldarbSolutions:
- Check
.envfile has valid API credentials - Verify network connectivity to Bybit
- Review startup logs for errors
- Check Docker resource limits (CPU/RAM)
Cause: Instrument IDs don't match Bybit's format.
Solution:
- Correct format:
PAXGUSDT-LINEAR.BYBIT(not-PERP) - Update
config_live.pyif needed - Rebuild Docker image:
docker-compose build goldarb
- NautilusTrader Docs: https://nautilustrader.io/docs
- NautilusTrader Discord: Join Community
- Bybit API Docs: https://bybit-exchange.github.io/docs
- Bybit Support: https://www.bybit.com/en-US/help-center
When reporting issues, include:
- Docker logs (
docker logs goldarb --tail 200) - Configuration file (redact API keys)
- Error messages
- Steps to reproduce
- Environment info (OS, Docker version)
Contributions are welcome! Here's how you can help:
- Fork the repository
- Create a feature branch
- Make your changes
- Test thoroughly (on testnet)
- Submit a pull request
- Follow PEP 8
- Use type hints
- Add docstrings for functions
- Keep lines under 100 characters
- Run
blackformatter
Before submitting:
# Run on testnet
BYBIT_TESTNET=true python run_live.py
# Check logs for errors
tail -f logs/paxg_xaut_grid.jsonIMPORTANT RISK DISCLOSURE
- Trading involves substantial risk of loss
- This strategy is for educational purposes only
- Past performance does not guarantee future results
- The authors are not responsible for any financial losses
- This is not financial advice
- Market Risk: Spread may widen unexpectedly
- Liquidity Risk: Positions may be difficult to exit
- Technical Risk: Software bugs, API failures
- Execution Risk: Slippage, partial fills
- Funding Risk: Negative funding rates on perpetual swaps
- Correlation Risk: PAXG and XAUT correlation may break down
- ✅ Only trade with capital you can afford to lose
- ✅ Understand the strategy before deploying
- ✅ Start with small position sizes
- ✅ Monitor actively, especially initially
- ✅ Have a plan for extreme scenarios
Use at your own risk.
This project is licensed under the GNU Lesser General Public License v3.0 (LGPL-3.0).
See the LICENSE file for full license text.
- GitHub: @Patrick-code-Bot
- Repository: GoldArb
- Issues: Report a bug
Built with:
- NautilusTrader - High-performance algorithmic trading platform
- Bybit - Cryptocurrency derivatives exchange
Special thanks to the NautilusTrader community for their excellent framework and support.
⭐ If this project helps you, consider giving it a star! ⭐
Made with ❤️ for the algorithmic trading community