The unofficial python interface for the WeBull API
Complete paper trading simulation environment that mirrors live trading functionality for testing strategies without real money. The paper trading client inherits from the main webull class with specialized methods for simulation.
Paper trading requires:
Specialized client class for paper trading operations.
class paper_webull(webull):
def __init__(self):
"""
Initialize paper trading client.
Inherits all functionality from main webull class but routes
trading operations to paper trading endpoints.
"""Usage example:
from webull import paper_webull
# Initialize paper trading client
paper_wb = paper_webull()
# Login (same credentials as live account)
paper_wb.login('your_email@example.com', 'your_password')Get paper account information and virtual portfolio details.
def get_account(self):
"""
Get paper account information and virtual portfolio summary.
Returns:
dict: Paper account details including:
- totalValue: Total virtual portfolio value
- cashBalance: Virtual cash balance
- buyingPower: Virtual buying power
- dayChange: Daily change in portfolio value
- totalProfitLoss: Total unrealized P&L
- positions: Number of positions held
"""
def get_account_id(self):
"""
Get paper trading account ID.
Returns:
str: Paper account identifier
"""Usage examples:
# Get paper account summary
paper_account = paper_wb.get_account()
print(f"Paper Portfolio Value: ${paper_account['totalValue']}")
print(f"Virtual Cash: ${paper_account['cashBalance']}")
print(f"Virtual Buying Power: ${paper_account['buyingPower']}")
print(f"Paper P&L: ${paper_account['totalProfitLoss']}")
# Get paper account ID
paper_account_id = paper_wb.get_account_id()
print(f"Paper Account ID: {paper_account_id}")Get all current positions in the paper trading account.
def get_positions(self):
"""
Get all current positions in paper account.
Returns:
list: List of paper position objects containing:
- ticker: Stock symbol information
- position: Number of shares (positive for long, negative for short)
- cost: Average cost basis per share
- marketValue: Current market value of position
- unrealizedProfitLoss: Unrealized gain/loss
- unrealizedProfitLossRate: Unrealized gain/loss percentage
- lastPrice: Last traded price
"""Usage example:
# Get paper positions
paper_positions = paper_wb.get_positions()
print("Paper Trading Positions:")
for position in paper_positions:
symbol = position['ticker']['symbol']
shares = position['position']
cost = position['cost']
market_value = position['marketValue']
pnl = position['unrealizedProfitLoss']
pnl_pct = position['unrealizedProfitLossRate']
print(f"{symbol}: {shares} shares @ ${cost:.2f}")
print(f" Market Value: ${market_value:.2f}")
print(f" P&L: ${pnl:.2f} ({pnl_pct:.2f}%)")Place, modify, and cancel orders in the paper trading environment.
def place_order(self, stock=None, tId=None, price=0, action='BUY', orderType='LMT', enforce='GTC', quant=0, outsideRegularTradingHour=True):
"""
Place a paper trading order.
Parameters:
- stock (str, optional): Stock symbol
- tId (int, optional): Ticker ID
- price (float): Order price for limit orders
- action (str): 'BUY' or 'SELL'
- orderType (str): Order type - 'LMT', 'MKT', 'STP', 'STP_LMT'
- enforce (str): Time in force - 'GTC', 'DAY', 'IOC', 'FOK'
- quant (int): Quantity of shares
- outsideRegularTradingHour (bool): Allow extended hours trading
Returns:
dict: Paper order placement result with order ID and status
"""
def modify_order(self, order, price=0, action='BUY', orderType='LMT', enforce='GTC', quant=0, outsideRegularTradingHour=True):
"""
Modify existing paper trading order.
Parameters:
- order (dict): Existing paper order to modify
- price (float): New order price
- action (str): New order action
- orderType (str): New order type
- enforce (str): New time in force
- quant (int): New quantity
- outsideRegularTradingHour (bool): New extended hours setting
Returns:
dict: Modification result
"""
def cancel_order(self, order_id):
"""
Cancel paper trading order.
Parameters:
- order_id (str): Paper order ID to cancel
Returns:
dict: Cancellation result
"""Usage examples:
# Place paper buy order
paper_order = paper_wb.place_order(
stock='AAPL',
price=150.00,
action='BUY',
orderType='LMT',
enforce='DAY',
quant=100
)
print(f"Paper order placed: {paper_order['orderId']}")
# Modify paper order
modified_order = paper_wb.modify_order(
order=paper_order,
price=148.00, # Lower the price
quant=150 # Increase quantity
)
# Cancel paper order
cancel_result = paper_wb.cancel_order(paper_order['orderId'])Get current and historical paper trading orders.
def get_current_orders(self):
"""
Get all current active paper trading orders.
Returns:
list: List of active paper order objects
"""
def get_history_orders(self, status='Cancelled', count=20):
"""
Get paper trading order history.
Parameters:
- status (str): Filter by status - 'Cancelled', 'Filled', 'All'
- count (int): Number of historical orders to retrieve
Returns:
list: List of historical paper order objects
"""Usage examples:
# Get active paper orders
active_paper_orders = paper_wb.get_current_orders()
print(f"Active paper orders: {len(active_paper_orders)}")
for order in active_paper_orders:
print(f"Order {order['orderId']}: {order['action']} {order['quantity']} {order['symbol']}")
# Get paper order history
paper_history = paper_wb.get_history_orders(status='Filled', count=10)
print("Recent filled paper orders:")
for order in paper_history:
print(f"{order['symbol']}: {order['action']} {order['quantity']} @ ${order['avgFilledPrice']}")class PaperTradingStrategy:
def __init__(self, paper_wb, initial_capital=100000):
self.paper_wb = paper_wb
self.initial_capital = initial_capital
self.trades = []
self.start_time = time.time()
def execute_strategy(self, symbol, signal, quantity=100):
"""Execute trading strategy based on signals."""
if signal == 'BUY':
self.buy_stock(symbol, quantity)
elif signal == 'SELL':
self.sell_stock(symbol, quantity)
def buy_stock(self, symbol, quantity):
"""Execute buy order in paper account."""
try:
# Get current price
quote = self.paper_wb.get_quote(stock=symbol)
current_price = float(quote['close'])
# Place market buy order
order = self.paper_wb.place_order(
stock=symbol,
action='BUY',
orderType='MKT',
quant=quantity
)
self.trades.append({
'timestamp': time.time(),
'symbol': symbol,
'action': 'BUY',
'quantity': quantity,
'price': current_price,
'order_id': order['orderId']
})
print(f"BUY {quantity} {symbol} @ ${current_price}")
except Exception as e:
print(f"Buy order failed: {e}")
def sell_stock(self, symbol, quantity):
"""Execute sell order in paper account."""
try:
# Check if we own the stock
positions = self.paper_wb.get_positions()
position = None
for pos in positions:
if pos['ticker']['symbol'] == symbol:
position = pos
break
if not position or position['position'] < quantity:
print(f"Insufficient shares of {symbol} to sell")
return
# Get current price
quote = self.paper_wb.get_quote(stock=symbol)
current_price = float(quote['close'])
# Place market sell order
order = self.paper_wb.place_order(
stock=symbol,
action='SELL',
orderType='MKT',
quant=quantity
)
self.trades.append({
'timestamp': time.time(),
'symbol': symbol,
'action': 'SELL',
'quantity': quantity,
'price': current_price,
'order_id': order['orderId']
})
print(f"SELL {quantity} {symbol} @ ${current_price}")
except Exception as e:
print(f"Sell order failed: {e}")
def get_performance_report(self):
"""Generate strategy performance report."""
account = self.paper_wb.get_account()
current_value = account['totalValue']
# Calculate returns
total_return = current_value - self.initial_capital
return_pct = (total_return / self.initial_capital) * 100
# Calculate trade statistics
total_trades = len(self.trades)
buy_trades = len([t for t in self.trades if t['action'] == 'BUY'])
sell_trades = len([t for t in self.trades if t['action'] == 'SELL'])
print("=== PAPER TRADING PERFORMANCE REPORT ===")
print(f"Initial Capital: ${self.initial_capital:,.2f}")
print(f"Current Value: ${current_value:,.2f}")
print(f"Total Return: ${total_return:,.2f} ({return_pct:.2f}%)")
print(f"Total Trades: {total_trades} (Buy: {buy_trades}, Sell: {sell_trades})")
# Show current positions
positions = self.paper_wb.get_positions()
if positions:
print("\nCurrent Positions:")
for pos in positions:
symbol = pos['ticker']['symbol']
shares = pos['position']
pnl = pos['unrealizedProfitLoss']
pnl_pct = pos['unrealizedProfitLossRate']
print(f" {symbol}: {shares} shares, P&L: ${pnl:.2f} ({pnl_pct:.2f}%)")
return {
'initial_capital': self.initial_capital,
'current_value': current_value,
'total_return': total_return,
'return_pct': return_pct,
'total_trades': total_trades
}
# Usage example
strategy = PaperTradingStrategy(paper_wb, initial_capital=50000)
# Simulate trading signals
symbols = ['AAPL', 'TSLA', 'MSFT']
for symbol in symbols:
strategy.execute_strategy(symbol, 'BUY', 50)
# Wait some time and take profits
time.sleep(60) # Wait 1 minute
for symbol in symbols:
strategy.execute_strategy(symbol, 'SELL', 25) # Sell half
# Get performance report
performance = strategy.get_performance_report()def moving_average_strategy(paper_wb, symbol, short_period=5, long_period=20):
"""
Implement simple moving average crossover strategy in paper account.
"""
# Get historical data
bars = paper_wb.get_bars(
stock=symbol,
interval='d1',
count=long_period + 10
)
if len(bars) < long_period:
print(f"Insufficient data for {symbol}")
return
# Calculate moving averages
closes = [float(bar['close']) for bar in bars]
def simple_ma(prices, period):
return sum(prices[-period:]) / period
short_ma = simple_ma(closes, short_period)
long_ma = simple_ma(closes, long_period)
# Previous MA values for crossover detection
prev_short_ma = simple_ma(closes[:-1], short_period)
prev_long_ma = simple_ma(closes[:-1], long_period)
current_price = closes[-1]
print(f"{symbol} Analysis:")
print(f"Current Price: ${current_price:.2f}")
print(f"Short MA ({short_period}): ${short_ma:.2f}")
print(f"Long MA ({long_period}): ${long_ma:.2f}")
# Check for crossover signals
if prev_short_ma <= prev_long_ma and short_ma > long_ma:
# Golden cross - buy signal
print("🟢 Golden Cross detected - BUY signal")
paper_wb.place_order(
stock=symbol,
action='BUY',
orderType='MKT',
quant=100
)
elif prev_short_ma >= prev_long_ma and short_ma < long_ma:
# Death cross - sell signal
print("🔴 Death Cross detected - SELL signal")
# Check if we have position to sell
positions = paper_wb.get_positions()
for pos in positions:
if pos['ticker']['symbol'] == symbol and pos['position'] > 0:
paper_wb.place_order(
stock=symbol,
action='SELL',
orderType='MKT',
quant=min(100, pos['position'])
)
break
else:
print("➡️ No signal - holding current position")
# Test strategy on multiple stocks
test_symbols = ['AAPL', 'TSLA', 'MSFT', 'NVDA']
for symbol in test_symbols:
moving_average_strategy(paper_wb, symbol)
print("-" * 40)from webull import paper_webull
import time
def paper_trading_demo():
"""Complete paper trading demonstration."""
# Initialize paper trading client
paper_wb = paper_webull()
try:
# Login
paper_wb.login('your_email@example.com', 'your_password')
print("Logged into paper trading account")
# Get initial account state
initial_account = paper_wb.get_account()
print(f"Starting Portfolio Value: ${initial_account['totalValue']}")
print(f"Starting Cash: ${initial_account['cashBalance']}")
# Test trading operations
test_symbol = 'AAPL'
# Get quote
quote = paper_wb.get_quote(stock=test_symbol)
current_price = float(quote['close'])
print(f"{test_symbol} current price: ${current_price}")
# Place buy order
buy_order = paper_wb.place_order(
stock=test_symbol,
price=current_price * 0.99, # Slightly below market
action='BUY',
orderType='LMT',
enforce='DAY',
quant=10
)
print(f"Buy order placed: {buy_order['orderId']}")
# Check order status
current_orders = paper_wb.get_current_orders()
print(f"Active orders: {len(current_orders)}")
# Wait a bit then modify order to market price
time.sleep(5)
if current_orders:
order_to_modify = current_orders[0]
modified_order = paper_wb.modify_order(
order=order_to_modify,
price=current_price, # Market price
quant=15 # Increase quantity
)
print(f"Order modified: {modified_order}")
# Wait for potential fill, then check positions
time.sleep(10)
positions = paper_wb.get_positions()
print(f"Current positions: {len(positions)}")
for pos in positions:
symbol = pos['ticker']['symbol']
shares = pos['position']
pnl = pos['unrealizedProfitLoss']
print(f"{symbol}: {shares} shares, P&L: ${pnl}")
# Place sell order if we have position
if positions:
for pos in positions:
if pos['ticker']['symbol'] == test_symbol and pos['position'] > 0:
sell_order = paper_wb.place_order(
stock=test_symbol,
price=current_price * 1.02, # Slightly above market
action='SELL',
orderType='LMT',
quant=pos['position']
)
print(f"Sell order placed: {sell_order['orderId']}")
break
# Final account status
final_account = paper_wb.get_account()
print(f"\nFinal Portfolio Value: ${final_account['totalValue']}")
print(f"Final Cash: ${final_account['cashBalance']}")
# Order history
history = paper_wb.get_history_orders(status='All', count=10)
print(f"\nOrder History ({len(history)} orders):")
for order in history:
print(f" {order['symbol']}: {order['action']} {order['quantity']} @ ${order.get('avgFilledPrice', 'N/A')}")
except Exception as e:
print(f"Paper trading error: {e}")
# Run the demo
paper_trading_demo()Key differences between paper and live trading:
Use paper trading to:
Remember to account for these differences when transitioning to live trading.
Paper trading includes access to social trading features for community interaction and learning from other traders.
Get social trading posts and discussions from the community.
def get_social_posts(self, topic, num=100):
"""
Get social trading posts for a specific topic.
Parameters:
- topic (str): Topic or symbol to get posts for
- num (int, optional): Number of posts to retrieve (default: 100)
Returns:
list: Social posts with user comments, likes, and engagement data
"""Access the social home feed with trending discussions and popular content.
def get_social_home(self, topic, num=100):
"""
Get social home feed content.
Parameters:
- topic (str): Topic filter for home feed content
- num (int, optional): Number of feed items to retrieve (default: 100)
Returns:
list: Home feed content with trending posts and discussions
"""Usage example:
from webull import paper_webull
paper_wb = paper_webull()
paper_wb.login('your_email@example.com', 'your_password')
# Get social posts for AAPL
aapl_posts = paper_wb.get_social_posts('AAPL', num=50)
print(f"Found {len(aapl_posts)} posts about AAPL")
for post in aapl_posts[:5]: # First 5 posts
print(f"User: {post.get('username', 'Anonymous')}")
print(f"Content: {post.get('content', '')[:100]}...")
print(f"Likes: {post.get('likes', 0)}")
print("-" * 30)
# Get home feed content
home_feed = paper_wb.get_social_home('trending', num=20)
print(f"Home feed has {len(home_feed)} trending items")Install with Tessl CLI
npx tessl i tessl/pypi-webull