CtrlK
BlogDocsLog inGet started
Tessl Logo

tessl/pypi-webull

The unofficial python interface for the WeBull API

Overview
Eval results
Files

paper-trading.mddocs/

Paper Trading

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.

Prerequisites

Paper trading requires:

  1. Webull account with paper trading enabled
  2. Successful login (same credentials as live account)

Capabilities

Paper Trading Client

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')

Paper Account Management

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}")

Paper Positions

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}%)")

Paper Order Management

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'])

Paper Order History

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']}")

Paper Trading Strategy Testing

Strategy Backtesting Framework

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()

Moving Average Crossover Strategy

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)

Complete Paper Trading Example

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()

Paper vs Live Trading Differences

Key differences between paper and live trading:

  1. Execution: Paper orders may fill at prices that wouldn't be available in live markets
  2. Slippage: Paper trading doesn't account for bid-ask spreads and market impact
  3. Liquidity: All paper orders assume unlimited liquidity
  4. Emotions: No real money means no emotional pressure
  5. Market Hours: Paper trading may have different hours than live markets

Use paper trading to:

  • Test strategies without risk
  • Learn the API and order types
  • Practice portfolio management
  • Validate trading algorithms

Remember to account for these differences when transitioning to live trading.

Social Trading Features

Paper trading includes access to social trading features for community interaction and learning from other traders.

Social Posts

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
    """

Social Home Feed

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

docs

alerts-screening.md

authentication.md

index.md

market-data.md

options.md

paper-trading.md

portfolio.md

streaming.md

trading.md

tile.json