FiveTwenty quick start¶
Read account data and prices, submit a practice market order, inspect exposure, and close an instrument position. Running all cells changes account state. The connection cell selects the first account returned by the token; check the resulting account ID before the order cell. Position closure affects all trades on the selected instrument, including trades opened outside this notebook.
Setup¶
Use a dedicated OANDA practice account and set both FIVETWENTY_OANDA_TOKEN and FIVETWENTY_OANDA_ACCOUNT in the kernel's environment. Review the configuration cell before making requests. Install the packages imported by the setup cell, select that Python environment as the Jupyter kernel, and run cells in order.
The repository's uv run poe docs-validate-notebooks command executes a temporary copy with synthetic HTTP responses. Running this notebook normally uses its configured API credentials; the offline validation result is not a live account test.
Setup and Imports¶
import asyncio
import os
from decimal import Decimal
from fivetwenty import AsyncClient, Environment
from fivetwenty.exceptions import FiveTwentyError
# Check if we're in Jupyter and enable async support
try:
import nest_asyncio
nest_asyncio.apply()
print("✅ Jupyter async support enabled")
except ImportError:
print("⚠️ Install nest_asyncio for better Jupyter support: uv add nest_asyncio")
# Configuration
TOKEN = os.getenv("FIVETWENTY_OANDA_TOKEN", "your-token-here")
ACCOUNT_ID = os.getenv("FIVETWENTY_OANDA_ACCOUNT", "your-account-id-here")
ENVIRONMENT = Environment.PRACTICE # Always use practice for demos!
if TOKEN == "your-token-here" or ACCOUNT_ID == "your-account-id-here":
print("⚠️ Please set your FIVETWENTY_OANDA_TOKEN and FIVETWENTY_OANDA_ACCOUNT environment variables")
else:
print("✅ Credentials configured")
1. Connect to OANDA¶
First, let's establish a connection and get our account information.
async def connect_to_oanda():
"""Connect to OANDA and get account info."""
async with AsyncClient(token=TOKEN, account_id=ACCOUNT_ID, environment=ENVIRONMENT) as client:
try:
# Get accounts
accounts = await client.accounts.get_accounts()
print(f"✅ Connected! Found {len(accounts)} account(s):")
for account in accounts:
tags = f" tags={account.tags}" if account.tags else ""
print(f" - {account.id}{tags}")
return accounts[0] if accounts else None
except FiveTwentyError as e:
print(f"❌ OANDA API Error: {e.message}")
return None
except Exception as e:
print(f"❌ Connection Error: {e}")
return None
# Connect and get account
account = await connect_to_oanda()
ACCOUNT_ID = account.id if account else ACCOUNT_ID
2. Check Account Balance¶
Let's check our account balance and margin information.
async def check_account_balance(account_id):
"""Check account balance and margin."""
if not account_id:
print("❌ No account ID available")
return None
async with AsyncClient(token=TOKEN, account_id=ACCOUNT_ID, environment=ENVIRONMENT) as client:
try:
account_response = await client.accounts.get_account(account_id)
account = account_response["account"]
print("💰 Account Balance Information:")
print(f" Balance: {account.balance} {account.currency}")
print(f" Unrealized P/L: {account.unrealized_pl} {account.currency}")
print(f" Margin Used: {account.margin_used} {account.currency}")
print(f" Margin Available: {account.margin_available} {account.currency}")
print(f" Open Trades: {account.open_trade_count}")
print(f" Open Positions: {account.open_position_count}")
return account
except FiveTwentyError as e:
print(f"❌ Error getting account: {e.message}")
return None
# Check balance
account_info = await check_account_balance(ACCOUNT_ID)
3. Get Current Prices¶
Let's fetch current market prices for popular currency pairs.
async def get_current_prices(account_id, instruments):
"""Get current prices for instruments."""
if not account_id:
print("❌ No account ID available")
return None
async with AsyncClient(token=TOKEN, account_id=ACCOUNT_ID, environment=ENVIRONMENT) as client:
try:
pricing_response = await client.pricing.get_pricing(account_id=account_id, instruments=instruments)
prices = pricing_response["prices"]
print("📈 Current Market Prices:")
for price in prices:
if price.bids and price.asks:
bid = price.bids[0].price
ask = price.asks[0].price
spread = Decimal(ask) - Decimal(bid)
print(f" {price.instrument}:")
print(f" Bid: {bid}")
print(f" Ask: {ask}")
print(f" Spread: {spread:.5f}")
print(f" Time: {price.time}")
print()
return prices
except FiveTwentyError as e:
print(f"❌ Error getting prices: {e.message}")
return None
# Get prices for major pairs
instruments = ["EUR_USD", "GBP_USD", "USD_JPY"]
current_prices = await get_current_prices(ACCOUNT_ID, instruments)
4. Place a Market Order¶
The next cell submits a market order to the selected practice account. Its fixed size is an example, not a recommendation. Check the account ID, existing exposure and returned fill before continuing.
async def place_market_order(account_id, instrument="EUR_USD", units=1000):
"""Place a market order."""
if not account_id:
print("❌ No account ID available")
return None
print(f"🚀 Placing {units} unit order for {instrument}...")
async with AsyncClient(token=TOKEN, account_id=ACCOUNT_ID, environment=ENVIRONMENT) as client:
try:
# Place market order
order_response = await client.orders.post_market_order(account_id=account_id, instrument=instrument, units=units)
if order_response.order_fill_transaction:
fill = order_response.order_fill_transaction
print("✅ Order Filled!")
print(f" Order ID: {fill.id}")
print(f" Instrument: {fill.instrument}")
print(f" Units: {fill.units}")
print(f" Price: {fill.price}")
print(f" P/L: {fill.pl} {fill.account_balance}")
print(f" Time: {fill.time}")
return fill
print("❌ Order was not filled")
print(order_response)
return None
except FiveTwentyError as e:
print(f"❌ Error placing order: {e.message}")
print(f"Error code: {e.code}")
return None
# Place a small buy order for EUR/USD
# WARNING: This will place a real order in your practice account!
print("⚠️ About to place a practice trade...")
order_fill = await place_market_order(ACCOUNT_ID, "EUR_USD", 1000)
5. Check Open Positions¶
Let's see what positions we have open after our trade.
async def check_open_positions(account_id):
"""Check open positions."""
if not account_id:
print("❌ No account ID available")
return None
async with AsyncClient(token=TOKEN, account_id=ACCOUNT_ID, environment=ENVIRONMENT) as client:
try:
positions_response = await client.positions.get_open_positions(account_id)
positions = positions_response["positions"]
if positions:
print(f"📊 Open Positions ({len(positions)}):")
for position in positions:
print(f"\n {position.instrument}:")
if position.long.units != 0:
print(f" Long: {position.long.units} units")
print(f" Average Price: {position.long.average_price}")
print(f" Unrealized P/L: {position.long.unrealized_pl}")
if position.short.units != 0:
print(f" Short: {position.short.units} units")
print(f" Average Price: {position.short.average_price}")
print(f" Unrealized P/L: {position.short.unrealized_pl}")
else:
print("📊 No open positions")
return positions
except FiveTwentyError as e:
print(f"❌ Error getting positions: {e.message}")
return None
# Check positions
positions = await check_open_positions(ACCOUNT_ID)
6. Close Position (Optional)¶
This closes all open long and short trades for EUR/USD in the account, including any that predate this notebook. The position-close endpoint avoids opening an opposite hedge.
async def close_position(account_id, instrument="EUR_USD"):
"""Close an open position."""
if not account_id:
print("❌ No account ID available")
return None
async with AsyncClient(token=TOKEN, account_id=account_id, environment=ENVIRONMENT) as client:
try:
# Get current position
positions_response = await client.positions.get_open_positions(account_id)
positions = positions_response["positions"]
position = next((p for p in positions if p.instrument == instrument), None)
if not position:
print(f"❌ No open position for {instrument}")
return None
# Close each open side explicitly, including both sides on hedging accounts.
has_long = position.long.units != 0
has_short = position.short.units != 0
if not has_long and not has_short:
print(f"❌ No position to close for {instrument}")
return None
close_response = await client.positions.close_position(
account_id=account_id,
instrument=instrument,
long_units="ALL" if has_long else "NONE",
short_units="ALL" if has_short else "NONE",
)
for side, requested in (("long", has_long), ("short", has_short)):
if not requested:
continue
fill = close_response.get(f"{side}OrderFillTransaction")
if fill is None:
print(f"❌ {side.title()} closure did not fill; check remaining positions")
return None
print(f"✅ Closed {side} side: {fill.units} units at {fill.price}, realized P/L {fill.pl}")
return close_response
except FiveTwentyError as e:
print(f"❌ Error closing position: {e.message}")
return None
# Uncomment the line below to close the EUR/USD position
# close_result = await close_position(ACCOUNT_ID, "EUR_USD")
print("💡 Uncomment the line above to close your EUR/USD position")
7. Stream Real-time Prices (Optional)¶
Let's stream real-time prices for a few seconds to see live market data.
async def stream_prices_demo(account_id, instruments, duration=10):
"""Stream prices for a limited time."""
if not account_id:
print("❌ No account ID available")
return
print(f"📡 Streaming prices for {duration} seconds...")
print("Press Ctrl+C to stop early\n")
async with AsyncClient(token=TOKEN, account_id=ACCOUNT_ID, environment=ENVIRONMENT) as client:
try:
start_time = asyncio.get_event_loop().time()
price_count = 0
async for event in client.pricing.get_pricing_stream(account_id, instruments):
elapsed = asyncio.get_event_loop().time() - start_time
if elapsed > duration:
print(f"\n⏰ Stream completed after {duration} seconds")
break
if event.type == "PRICE":
price_count += 1
if event.bids and event.asks:
bid = event.bids[0].price
ask = event.asks[0].price
print(f"📈 {event.instrument}: {bid}/{ask} (#{price_count})")
elif event.type == "HEARTBEAT":
print(f"💓 Heartbeat at {event.time}")
print(f"\n✅ Received {price_count} price updates")
except KeyboardInterrupt:
print("\n⏹️ Stream stopped by user")
except FiveTwentyError as e:
print(f"❌ Streaming error: {e.message}")
# Uncomment to stream live prices
# await stream_prices_demo(ACCOUNT_ID, ["EUR_USD"], duration=10)
print("💡 Uncomment the line above to stream live prices for 10 seconds")
Interpreting the results¶
Check the printed inputs, timestamps and handled errors before interpreting output. A completed cell can still report an API failure. Example calculations and simulations do not establish a profitable strategy, a guaranteed loss cap or readiness for unattended execution.
For further work, test boundary cases, currency conversion, incomplete data and recovery after a disconnect. Keep signal evaluation separate from order submission and reconcile any account changes.