Backtest a gold strategy with historical OHLC
Pull daily OHLC history from the goldprice.dev API and run a simple moving-average crossover backtest in Python to evaluate a gold trading strategy.
Read →A complete, production-ready Python script that polls the live spot price, checks it against a threshold, and sends a notification. Uses the REST API with retry logic and a local TTL cache.
This tutorial builds a standalone Python script that polls the live gold spot price and fires an alert when a threshold is crossed. It:
No SDKs required—only the standard library plus requests.
pip install requestsThe spot endpoint returns the current price along with source metadata:
import requests
API_KEY = "YOUR_API_KEY"
BASE_URL = "https://api.goldprice.dev/v1"
def fetch_spot_price() -> float:
"""Return the current XAU-USD spot price in USD per troy ounce."""
resp = requests.get(
f"{BASE_URL}/prices",
params={"symbol": "XAU-USD-SPOT"},
headers={"Authorization": f"Bearer {API_KEY}"},
timeout=10,
)
resp.raise_for_status()
data = resp.json()
# The API returns sources[]. Use the first live spot source.
for source in data.get("sources", []):
if source.get("type") == "spot" and not source.get("stale", True):
return float(source["price"])
raise ValueError("No live spot price in response")
The sources[] array lets you inspect which oracle is behind each price. If a source is flagged stale: true, skip it—the API may return multiple sources and some may be delayed.
Polling every second against a REST endpoint is wasteful and burns quota. A simple in-memory cache with a configurable TTL is enough for most alert use cases:
import time
from typing import Optional
_cache: dict[str, tuple[float, float]] = {} # key -> (value, expiry)
def cached_spot_price(ttl_seconds: int = 60) -> float:
"""Return a cached spot price, refreshing only when TTL has elapsed."""
key = "XAU-USD-SPOT"
now = time.monotonic()
if key in _cache:
value, expiry = _cache[key]
if now < expiry:
return value
price = fetch_spot_price()
_cache[key] = (price, now + ttl_seconds)
return price
At 60-second TTL you use at most 1,440 calls per day—well within the free tier (1,000/month) for light testing, and comfortable for any paid plan. For a deeper look at cache strategy and rate-limit headers, see Caching gold prices and staying inside rate limits.
Network failures and rate-limit responses (HTTP 429) are transient. Wrap the fetch with exponential backoff:
import time
import requests
MAX_RETRIES = 4
BACKOFF_BASE = 2.0 # seconds
def fetch_spot_price_with_retry() -> float:
for attempt in range(MAX_RETRIES):
try:
return fetch_spot_price()
except requests.exceptions.HTTPError as exc:
if exc.response is not None and exc.response.status_code == 429:
wait = BACKOFF_BASE ** attempt
print(f"Rate limited. Retrying in {wait:.0f}s…")
time.sleep(wait)
else:
raise
except requests.exceptions.RequestException as exc:
wait = BACKOFF_BASE ** attempt
print(f"Request error: {exc}. Retrying in {wait:.0f}s…")
time.sleep(wait)
raise RuntimeError("Max retries exceeded fetching spot price")
Define thresholds and fire a notification. Here we use print as the notifier—swap in email, Slack, SMS, or any webhook:
def send_alert(message: str) -> None:
"""Replace this with your preferred notification channel."""
print(f"[ALERT] {message}")
def check_and_alert(
above: Optional[float] = None,
below: Optional[float] = None,
) -> None:
"""Fire an alert if the live price crosses either threshold."""
price = cached_spot_price(ttl_seconds=60)
print(f"XAU-USD spot: ${price:,.2f}")
if above is not None and price >= above:
send_alert(f"Gold crossed ABOVE ${above:,.0f} — current: ${price:,.2f}")
if below is not None and price <= below:
send_alert(f"Gold fell BELOW ${below:,.0f} — current: ${price:,.2f}")
Wire everything into a polling loop:
import time
def run_alert_loop(
above: Optional[float] = None,
below: Optional[float] = None,
poll_interval: int = 60,
) -> None:
"""
Poll forever, checking thresholds on each tick.
Args:
above: Fire when price >= this value (USD/oz). None = no upper alert.
below: Fire when price <= this value (USD/oz). None = no lower alert.
poll_interval: Seconds between checks (minimum 60 recommended).
"""
print(f"Starting gold alert. above={above}, below={below}, interval={poll_interval}s")
while True:
try:
check_and_alert(above=above, below=below)
except Exception as exc:
print(f"Error during check: {exc}")
time.sleep(poll_interval)
if __name__ == "__main__":
# Alert if gold goes above $3,500 or below $3,000
run_alert_loop(above=3500, below=3000, poll_interval=60)
Putting it all together in gold_alert.py:
"""
gold_alert.py — Poll the live gold spot price and fire threshold alerts.
Requires: pip install requests
"""
import time
from typing import Optional
import requests
API_KEY = "YOUR_API_KEY"
BASE_URL = "https://api.goldprice.dev/v1"
MAX_RETRIES = 4
BACKOFF_BASE = 2.0
_cache: dict[str, tuple[float, float]] = {}
def fetch_spot_price() -> float:
resp = requests.get(
f"{BASE_URL}/prices",
params={"symbol": "XAU-USD-SPOT"},
headers={"Authorization": f"Bearer {API_KEY}"},
timeout=10,
)
resp.raise_for_status()
data = resp.json()
for source in data.get("sources", []):
if source.get("type") == "spot" and not source.get("stale", True):
return float(source["price"])
raise ValueError("No live spot price in response")
def fetch_spot_price_with_retry() -> float:
for attempt in range(MAX_RETRIES):
try:
return fetch_spot_price()
except requests.exceptions.HTTPError as exc:
if exc.response is not None and exc.response.status_code == 429:
wait = BACKOFF_BASE ** attempt
time.sleep(wait)
else:
raise
except requests.exceptions.RequestException:
time.sleep(BACKOFF_BASE ** attempt)
raise RuntimeError("Max retries exceeded")
def cached_spot_price(ttl_seconds: int = 60) -> float:
key = "XAU-USD-SPOT"
now = time.monotonic()
if key in _cache:
value, expiry = _cache[key]
if now < expiry:
return value
price = fetch_spot_price_with_retry()
_cache[key] = (price, now + ttl_seconds)
return price
def send_alert(message: str) -> None:
print(f"[ALERT] {message}")
def check_and_alert(
above: Optional[float] = None,
below: Optional[float] = None,
) -> None:
price = cached_spot_price(ttl_seconds=60)
print(f"XAU-USD spot: ${price:,.2f}")
if above is not None and price >= above:
send_alert(f"Gold crossed ABOVE ${above:,.0f} — current: ${price:,.2f}")
if below is not None and price <= below:
send_alert(f"Gold fell BELOW ${below:,.0f} — current: ${price:,.2f}")
def run_alert_loop(
above: Optional[float] = None,
below: Optional[float] = None,
poll_interval: int = 60,
) -> None:
print(f"Starting gold alert. above={above}, below={below}, interval={poll_interval}s")
while True:
try:
check_and_alert(above=above, below=below)
except Exception as exc:
print(f"Error: {exc}")
time.sleep(poll_interval)
if __name__ == "__main__":
run_alert_loop(above=3500, below=3000, poll_interval=60)
The send_alert function is the only thing you need to swap out. A few common patterns:
Slack webhook:
import requests as req
def send_alert(message: str) -> None:
req.post(
"https://hooks.slack.com/services/YOUR/WEBHOOK",
json={"text": message},
timeout=5,
)
Email via SMTP:
import smtplib
from email.message import EmailMessage
def send_alert(message: str) -> None:
msg = EmailMessage()
msg["Subject"] = "Gold Price Alert"
msg["From"] = "alerts@yourdomain.com"
msg["To"] = "you@yourdomain.com"
msg.set_content(message)
with smtplib.SMTP_SSL("smtp.yourdomain.com", 465) as s:
s.login("alerts@yourdomain.com", "YOUR_PASSWORD")
s.send_message(msg)
| Plan | Calls/month | At 60s poll | Days of coverage |
|---|---|---|---|
| Free | 1,000 | ~1,440/day | < 1 day (demo only) |
| Basic | included | ~1,440/day | 30 days |
| Pro | included | ~1,440/day | 30 days |
For continuous 60-second polling, any paid plan is appropriate. Free tier is useful for testing the integration before upgrading. See the pricing page for plan details.
// related guides
Pull daily OHLC history from the goldprice.dev API and run a simple moving-average crossover backtest in Python to evaluate a gold trading strategy.
Read →Pull gold and silver spot prices in a single async call, divide them, and display the ratio with a working JavaScript example.
Read →How to convert a USD per troy ounce gold price into other currencies and per-gram units, with a working JavaScript example.
Read →// goldprice.dev
Live gold prices, historical OHLC, and multi-source aggregation — available via REST and SSE.