Metadata-Version: 2.1
Name: WhisperDriver
Version: 0.1.8
Summary: Automation and API library for WhisperTrades.com
Home-page: https://github.com/PaulNobrega/WhisperDriver
Author: Paul Nobrega
Author-email: Paul@PaulNobrega.net
License: MIT
Description: # WhisperDriver
        
        WhisperDriver is a comprehensive Python library for automating and managing [WhisperTrades.com](https://whispertrades.com/) bots, variables, and broker connections. It combines robust API access with advanced Selenium-based web automation, enabling features not available through the API alone. The library is designed with a focus on reliability, scheduling, and rate-limit consciousness.
        
        ## Features
        - **API and Web Automation**: Use the official WhisperTrades API for fast, reliable access, and Selenium automation for advanced features (e.g., UI-only settings, Schwab broker renewal).
        - **API and Web Credentials**: Uses API token, WhisperTrades credentials, and Schwab credentials for different functions beyond the scope of WT API alone.
        - **Bot Management**: Enable, disable, update, open/close positions, and schedule bots programmatically.
        - **Throttle Management**: Optional throttle lets you set a minimum delay between API requests to help avoid rate limits. Throttle is enabled with 2 second delay by default, but you can disable or adjust the delay as needed.
        - **Scheduler**: Built-in scheduler for timed bot actions (see `example.py`).
        - **Built-in Automatic Schwab Broker Renewal Function**: Seamlessly renew Schwab connections using your proivided Schwab credentials.  Requires App Authenticator 2FA enabled on Schwab account.  First-time in-app confirmation required to enable remembered device.
        
        ## Installation
        
        ```bash
        pip install WhisperDriver
        ```
        
        Or, to install all development dependencies:
        ```bash
        pip install -r requirements.txt
        ```
        
        ## Authentication & Credentials
        - **API Token**: Obtain from your [WhisperTrades.com](https://whispertrades.com/) account. Required for all API-based functions.
        - **WhisperTrades Username/Password**: Required for Selenium-based web automation (e.g., UI-only features).
        - **Schwab Username/Password**: Required only for automatic Schwab broker renewal.
        
        ## Quick Start Example
        
        ```python
        import WhisperDriver
        import creds as personal
        
        WD = WhisperDriver.ApiWrapper(personal.WT_API_TOKEN)
        WD.via_selenium.enable(personal.USER, personal.PWD, is_verbose=True, is_headless=True)
        
        # Start the scheduler
        WD.scheduler.start()
        
        # Enable all bots at 9:25 AM Eastern
        WD.scheduler.add_task('9:25 AM', 'America/New_York', fxn=WD.bots.enable_all_bots)
        
        # Soft disable all bots at 4:05 PM Eastern using Selenium
        from functools import partial
        WD.scheduler.add_task('4:05 PM', 'America/New_York', fxn=partial(WD.via_selenium.enabled_to_soft_disabled_by_list, WD.bots.get_all_bot_numbers()))
        
        # Stop the scheduler at 4:30 PM
        WD.scheduler.stop_scheduler_at_time('4:30 PM', 'America/New_York')
        ```
        
        ## Function Reference & Examples
        
        ## Core Concepts
        
        ### WD Object (`WD = WhisperDriver.ApiWrapper(token)`)
        The main interface for all automation and API access. Instantiating `WD` gives you access to all major features:
        
        - `.throttle` — API rate limiter (see below for details)
        - `.endpts` — Endpoint handler for all API endpoints (raw API access)
        - `.scheduler` — Scheduler for timed tasks and automation
        - `.bots` — Bot management interface (see below)
        - `.variables` — Variable management interface
        - `.via_selenium` — Selenium-based UI automation interface
        - `.bot_number_list` — List of all bot numbers
        - `.report_number_list` — List of all report numbers
        - `.variable_number_list` — List of all variable numbers
        
        ### WD.endpts: Endpoint Handler Object
        - `.bots` — All bot-related API endpoints:
            - `get_all_bots()` — Get all bots (full details)
            - `get_bot(bot_number, status_filter=['Enabled', 'Disabled', 'Disable on Close'], include_details=True)` — Get a specific bot or filtered list
            - `enable_bot(bot_number)` — Enable a bot
            - `disable_bot(bot_number)` — Disable a bot (or set to 'Disable on Close' if open positions)
            - `get_bot_orders(bot_number)` — Get all orders for a bot
            - `get_bot_positions(bot_number='', position_number='', status='', from_date='', to_date='', page='')` — Get all or filtered positions for a bot
            - `open_position(bot_number)` — Force open a new position for a bot
            - `close_position(bot_number)` — Close all open positions for a bot
            - `close_bot_position(position_number)` — Close a specific position by number
        - `.variables` — All variable-related API endpoints:
            - `get_all_bot_variables()` — Get all variables
            - `get_bot_variables(variable_number='')` — Get a specific variable or all
            - `set_bot_variables(variable_number, variable_name, new_value)` — Set a variable's value
        - `.reports` — All report-related API endpoints:
            - `get_all_bot_reports()` — Get all reports
            - `get_bot_report(report_number='')` — Get a specific report or all
            - `update_bot_report(report_number, new_name='', new_start_date='', new_end_date='', run_until_latest_date=False)` — Update a report's details
            - `run_bot_report(report_number)` — Run a report
        - `.brokers` — All broker connection endpoints:
            - `get_all_broker_connections()` — Get all broker connections
            - `get_broker_connections(number='')` — Get a specific broker connection or all
            - `rebalance_broker_collateral(number)` — Rebalance collateral for a broker connection
        
        **Example:**
        ```python
        # Get all bots (raw API)
        raw_bots = WD.endpts.bots.get_all_bots()
        # Enable a bot
        WD.endpts.bots.enable_bot('123456')
        # Get all variables
        all_vars = WD.endpts.variables.get_all_bot_variables()
        # Run a report
        WD.endpts.reports.run_bot_report('report123')
        # Get all broker connections
        brokers = WD.endpts.brokers.get_all_broker_connections()
        ```
        
        ### WD.throttle: Throttle Object
        - `.set_delay_sec(seconds)` — Set the minimum delay (in seconds) between API requests. (Default is 2 seconds)
        - `.enable()` / `.disable()` — Enable or disable the throttle.
        
        **Example:**
        ```python
        WD.throttle.set_delay_sec(2)
        WD.throttle.disable()
        WD.throttle.enable()
        ```
        
        ### WD.scheduler: Scheduler Object
        - `.start()` — Start the scheduler loop (runs in a background thread)
        - `.stop()` — Stop the scheduler loop
        - `.add_task(time_str, tz, fxn)` — Schedule any function to run at a specific time and timezone
        - `.stop_scheduler_at_time(time_str, tz)` — Stop the scheduler at a specific time
        - `.scheduler_is_on` — Boolean, True if the scheduler is running
        
        **Example:**
        ```python
        WD.scheduler.add_task('9:30 AM', 'America/New_York', fxn=WD.bots.enable_all_bots)
        WD.scheduler.start()
        ```
        
        ### WD.variables: Variable Management Object
        - `.get_all_variables()` — Get all account variables
        - `.update_variable(var_name, value)` — Update a variable
        - (Other variable-related methods may be available)
        
        **Example:**
        ```python
        all_vars = WD.variables.get_all_variables()
        WD.variables.update_variable('MY_VAR', 42)
        ```
        
        ### WD.via_selenium: Selenium Automation Object
        - `.enable(user, pwd, is_verbose=True, is_headless=True)` — Log in to WhisperTrades web UI for advanced automation
        - `.enabled_to_soft_disabled_by_list(bot_nums, time_str=None, tz=None)` — Instantly soft-disables a list of bots via the web UI
        - `.renew_schwab_connection(schwab_user, schwab_pwd)` — Automatically renew Schwab broker connections (requires Schwab credentials)
        - (Other UI automation methods may be available)
        
        **Example:**
        ```python
        WD.via_selenium.enable('myuser', 'mypassword', is_headless=True)
        WD.via_selenium.enabled_to_soft_disabled_by_list(['123456', '654321'])
        ```
        
        ## Bot Management
        
        ### Bots Object (`WD.bots`)
        - `WD.bots.bots_list` — All bot objects (list)
        - `WD.bots('bot_number')` — Returns bot object of bot number
        - `WD.bots.get_all_bot_variables()` — Queries for variables and associates variables with bot objects in `.variables`
        - `WD.bots.update_all_bots()` — Updates bot_list via WhisperTrades API
        
        #### BotsList Object (`WD.bots.bots_list`)
            - `()` or `.all()` — List of all bot objects
            - `.is_enabled()` — List of bot objects that are currently enabled
            - `.is_disabled()` — List of bot objects that are currently disabled
            - `.is_disabled_on_close()` — List of bot objects that are currently disabled-on-close
            - `.add_bot_to_list(bot_dict)` — Creates bot object from JSON and adds to bot_list
            - `.remove_bot_from_list('bot_number')` — Removes bot from bot_list
        
        #### Bot Object (`WD.bots('bot_number')`)
            - `.number` — Bot number
            - `.name` — Bot name
            - `.broker_connection` — Broker details dict
            - `.is_paper` — Bool, is sim or not
            - `.status` — Current enabled/disabled status
            - `.can_enable` — Bool, can enable
            - `.can_disable` — Bool, can disable
            - `.symbol` — Ticker bot is trading
            - `.type` — Position type
            - `.notes` — User notes
            - `.last_active_at` — Time last active
            - `.disabled_at` — Time disabled at
            - `.entry_condition` — Entry settings dict
            - `.exit_condition` — Exit settings dict
            - `.adjustments` — List of adjustment conditions
            - `.notifications` — List of notifications
            - `.variables` — List of bot variables
            - `.enable()` — Enable bot immediately
            - `.disable()` — Disable bot immediately
            - `.enable_at_time(time_str, tz_str='America/New_York')` — Schedule this bot to be enabled at a specific time and timezone
            - `.disable_at_time(time_str, tz_str='America/New_York')` — Schedule this bot to be disabled at a specific time and timezone
            - `.get_positions(position_number='', status='', from_date='', to_date='', page='')` — Get all positions for this bot, or use filter arguments
            - `.close_position_by_number(position_number)` — Close position by number
            - `.get_orders()` — Get all orders for bot
            - `.open_position()` — If bot is enabled, force open a new position
            - `.close_position()` — Close all bot’s open positions
            - `.update()` — Update bot information from WhisperTrades API
            - `.get_bot_variables()` — Update bot variable information from WhisperTrades API
        
        ## Entry Filter Fields (Selenium UI Automation)
        
        The following entry filter fields are available for automation and scraping via Selenium (see `SeleniumDriver.get_entry_settings` and `update_entry_settings`). These correspond to the UI fields in the WhisperTrades bot entry form. All fields below can be read and set using the SeleniumDriver's entry settings methods.
        
        | Field Name | Description |
        |---|---|
        | frequency | Entry frequency (e.g., Daily, Weekly) |
        | maximum_entries_per_day | Maximum entries per day |
        | maximum_concurrent_positions | Maximum concurrent positions |
        | day_of_week | Day(s) of week to allow entry |
        | allocation_type | Allocation type (e.g., contracts, percent) |
        | contract_quantity | Number of contracts to allocate |
        | leverage_amount | Leverage amount |
        | percent_of_portfolio | Percent of portfolio to allocate |
        | minimum_days_to_expiration | Minimum days to expiration |
        | target_days_to_expiration | Target days to expiration |
        | maximum_days_to_expiration | Maximum days to expiration |
        | put_short_strike_type | Put short strike target type |
        | put_short_strike_minimum_delta | Put short strike minimum delta |
        | put_short_strike_target_delta | Put short strike target delta |
        | put_short_strike_maximum_delta | Put short strike maximum delta |
        | put_short_strike_minimum_premium | Put short strike minimum premium |
        | put_short_strike_target_premium | Put short strike target premium |
        | put_short_strike_maximum_premium | Put short strike maximum premium |
        | put_short_strike_percent_otm_minimum | Put short strike minimum % OTM |
        | put_short_strike_target_percent_otm | Put short strike target % OTM |
        | put_short_strike_percent_otm_maximum | Put short strike maximum % OTM |
        | put_long_strike_type | Put long strike target type |
        | put_long_strike_target_delta | Put long strike target delta |
        | restrict_put_spread_width_by | Restrict put spread width by (points/percent) |
        | put_spread_minimum_width_points | Put spread minimum width (points) |
        | put_spread_maximum_width_points | Put spread maximum width (points) |
        | put_spread_minimum_width_percent | Put spread minimum width (percent) |
        | put_spread_maximum_width_percent | Put spread maximum width (percent) |
        | put_spread_target_width_points | Put spread target width (points) |
        | put_spread_strike_target_premium | Put spread strike target premium |
        | put_spread_target_width_percent | Put spread target width (percent from main strike) |
        | put_spread_smart_width | Use smart width for put spread |
        | call_short_strike_type | Call short strike target type |
        | call_short_strike_minimum_delta | Call short strike minimum delta |
        | call_short_strike_target_delta | Call short strike target delta |
        | call_short_strike_maximum_delta | Call short strike maximum delta |
        | call_short_strike_minimum_premium | Call short strike minimum premium |
        | call_short_strike_target_premium | Call short strike target premium |
        | call_short_strike_maximum_premium | Call short strike maximum premium |
        | call_short_strike_percent_otm_minimum | Call short strike minimum % OTM |
        | call_short_strike_target_percent_otm | Call short strike target % OTM |
        | call_short_strike_percent_otm_maximum | Call short strike maximum % OTM |
        | call_long_strike_type | Call long strike target type |
        | call_long_strike_target_delta | Call long strike target delta |
        | restrict_call_spread_width_by | Restrict call spread width by (points/percent) |
        | call_spread_minimum_width_points | Call spread minimum width (points) |
        | call_spread_maximum_width_points | Call spread maximum width (points) |
        | call_spread_minimum_width_percent | Call spread minimum width (percent) |
        | call_spread_maximum_width_percent | Call spread maximum width (percent) |
        | call_spread_target_width_points | Call spread target width (points) |
        | call_spread_strike_target_premium | Call spread strike target premium |
        | call_spread_target_width_percent | Call spread target width (percent from main strike) |
        | call_spread_smart_width | Use smart width for call spread |
        | minimum_starting_premium | Minimum premium for entry |
        | maximum_starting_premium | Maximum premium for entry |
        | minimum_iv | Minimum IV |
        | maximum_iv | Maximum IV |
        | minimum_vix | Minimum VIX |
        | maximum_vix | Maximum VIX |
        | minimum_underlying_percent_move_from_close | Minimum % move from previous close |
        | maximum_underlying_percent_move_from_close | Maximum % move from previous close |
        | minimum_underlying_percent_move_from_open | Minimum % move from open |
        | maximum_underlying_percent_move_from_open | Maximum % move from open |
        | entry_percent_from_today_high_toggle | Enable percent from today's high filter |
        | minimum_underlying_percent_move_from_today_high | Minimum % move from today's high |
        | maximum_underlying_percent_move_from_today_high | Maximum % move from today's high |
        | underlying_percent_from_today_high_start_time | Start time for today's high filter |
        | underlying_percent_from_today_high_end_time | End time for today's high filter |
        | entry_percent_from_today_low_toggle | Enable percent from today's low filter |
        | minimum_underlying_percent_move_from_today_low | Minimum % move from today's low |
        | maximum_underlying_percent_move_from_today_low | Maximum % move from today's low |
        | underlying_percent_from_today_low_start_time | Start time for today's low filter |
        | underlying_percent_from_today_low_end_time | End time for today's low filter |
        | entry_percent_from_prior_high_toggle | Enable percent from prior high filter |
        | minimum_underlying_percent_move_from_prior_high | Minimum % move from prior high |
        | maximum_underlying_percent_move_from_prior_high | Maximum % move from prior high |
        | underlying_percent_from_prior_high_start_time | Start time for prior high filter |
        | underlying_percent_from_prior_high_end_time | End time for prior high filter |
        | entry_percent_from_prior_low_toggle | Enable percent from prior low filter |
        | minimum_underlying_percent_move_from_prior_low | Minimum % move from prior low |
        | maximum_underlying_percent_move_from_prior_low | Maximum % move from prior low |
        | underlying_percent_from_prior_low_start_time | Start time for prior low filter |
        | underlying_percent_from_prior_low_end_time | End time for prior low filter |
        | entry_ma_crossover_toggle | Enable moving average crossover filter |
        | ma_crossover_one | First moving average for crossover |
        | ma_crossover_percent | Percent for crossover filter |
        | ma_crossover_type | Crossover type (above/below) |
        | ma_crossover_two | Second moving average for crossover |
        | entry_ma_value_toggle | Enable moving average value filter |
        | ma_value_percent | Percent for MA value filter |
        | ma_value_type | MA value type |
        | ma_value_ma | Moving average for value filter |
        | avoid_fomc | Avoid FOMC days |
        | avoid_fomc_days_before | Days before FOMC to avoid |
        | avoid_fomc_days_after | Days after FOMC to avoid |
        | min_underlying_price | Minimum underlying price |
        | max_underlying_price | Maximum underlying price |
        | min_underlying_iv | Minimum underlying IV |
        | max_underlying_iv | Maximum underlying IV |
        | min_underlying_iv_rank | Minimum underlying IV rank |
        | max_underlying_iv_rank | Maximum underlying IV rank |
        | min_underlying_iv_percentile | Minimum underlying IV percentile |
        | max_underlying_iv_percentile | Maximum underlying IV percentile |
        | skip_earnings | Skip earnings dates |
        | exclude_tickers | Exclude these tickers |
        | only_tickers | Only include these tickers |
        | min_underlying_price_change | Minimum underlying price change |
        | max_underlying_price_change | Maximum underlying price change |
        | min_underlying_volume | Minimum underlying volume |
        | min_underlying_open_interest | Minimum underlying open interest |
        | min_underlying_market_cap | Minimum underlying market cap |
        | underlying_sector | Underlying sector filter |
        | underlying_industry | Underlying industry filter |
        | only_etfs | Only include ETFs |
        | only_stocks | Only include stocks |
        | only_index | Only include index products |
        | only_liquid_options | Only include liquid options |
        | only_marginable | Only include marginable securities |
        | only_shortable | Only include shortable securities |
        | only_easy_to_borrow | Only include easy-to-borrow securities |
        | only_hard_to_borrow | Only include hard-to-borrow securities |
        | custom_filter | Custom filter (advanced) |
        | entry_speed | Entry speed (e.g., Fast, Normal) |
        | move_strike_selection_with_conflict | Move strike selection if conflict |
        
        For the full, up-to-date list and technical details, see the `field_map` in `via_ui.py`.
        
        **Usage Examples:**
        ```python
        # List all bots
        all_bots = WD.bots.bots_list.all
        
        # Get a specific bot object
        bot = WD.bots('123456')
        print(bot.name, bot.status)
        
        # Get enabled bots
        enabled_bots = WD.bots.bots_list.is_enabled()
        
        # Enable/disable a bot immediately
        bot.enable()
        bot.disable()
        
        # Schedule enable/disable for a bot at a specific time
        bot.enable_at_time('09:25 AM', 'America/New_York')
        bot.disable_at_time('04:05 PM', 'America/New_York')
        
        # Correct usage for scheduling multiple bots:
        _ = [WD.bots(b).enable_at_time('09:25 AM', 'America/New_York') for b in bot_list]
        _ = [WD.bots(b).disable_at_time('04:05 PM', 'America/New_York') for b in bot_list]
        
        # Get positions, orders, and variables
        positions = bot.get_positions()
        orders = bot.get_orders()
        variables = bot.get_bot_variables()
        
        # Open/close positions
        bot.open_position()
        bot.close_position()
        bot.close_position_by_number('pos123')
        
        # Update bot info
        bot.update()
        ```
        
        **Usage Examples:**
        ```python
        # Get a list of all bot objects
        all_bots = WD.bots.bots_list.all
        for bot in all_bots:
            print(bot.number, bot.name, bot.status)
        
        # Get all enabled bots
        enabled_bots = WD.bots.bots_list.is_enabled()
        
        # Refresh the list of all bots from the server
        WD.bots.update_all_bots()
        
        # Enable or disable all bots
        WD.bots.enable_all_bots()
        WD.bots.disable_all_bots()
        
        ### Variable Management
        - **`WD.variables.get_all_variables()`**: Get all account variables.
        - **`WD.variables.update_variable(var_name, value)`**: Update a variable.
        
        ### Throttle
         **`WD.throttle.set_delay_sec(seconds)`**: Set the minimum delay (in seconds) between API requests. (Default is 2 seconds)
         **`WD.throttle.enable()` / `WD.throttle.disable()`**: Enable or disable the throttle.
        
        #### Example: Throttle Usage
        ```python
        WD.throttle.set_delay_sec(2)
        WD.throttle.set_delay(2)
        # Disable throttle if you want maximum speed (not recommended for production)
        WD.throttle.disable()
        # Enable throttle again
        WD.throttle.enable()
        ```
        
        ### Scheduler
        - **`WD.scheduler.add_task(time_str, tz, fxn)`**: Schedule any function to run at a specific time and timezone.
        - **`WD.scheduler.start()`**: Start the scheduler loop.
        - **`WD.scheduler.stop_scheduler_at_time(time_str, tz)`**: Stop the scheduler at a specific time.
        
        #### Example: Per-Bot Scheduling
        ```python
        from functools import partial
        for bot in WD.bots.get_all_bots():
            entry_time = bot['entry_time']  # e.g., '9:45 AM'
            bot_num = bot['number']
            # Enable 5 min before entry
            WD.scheduler.add_task('9:40 AM', 'America/New_York', fxn=partial(WD.bots.enable_bot, bot_num))
            # Soft disable 5 min after entry
            WD.scheduler.add_task('9:50 AM', 'America/New_York', fxn=partial(WD.via_selenium.enabled_to_soft_disabled_by_list, [bot_num]))
        ```
        
        ## Notes
        - **Selenium Automation**: Some features (like Schwab renewal and soft disable) require a running Chrome/Chromium browser. Headless mode is supported for servers.
        - **Security**: Keep your credentials secure. Never share your API token or passwords.
        
        ## Troubleshooting
        - If Selenium functions fail on a server, ensure Chrome/Chromedriver and all dependencies are installed, and use headless mode.
        - For 2FA (e.g., Schwab SMS), you may need to provide the code interactively if prompted.  If only sms 2FA you will need to instantiate with headless=False and provide the texted code
        
        ## License
        MIT License
        
        
Platform: UNKNOWN
Classifier: Programming Language :: Python :: 3
Classifier: License :: OSI Approved :: MIT License
Classifier: Operating System :: OS Independent
Requires-Python: >=3.10
Description-Content-Type: text/markdown
