Repository: tfukaza/harvest (language: python)

Issue:
Improve website design [📝Documentation]
The current website has several issue that needs to be addressed:
- [x] `Algo`, `Trader`, `BackTester` classes are not fully documented. Update `parser.py` as well as necessary website components to document all public APIs of these classes. 
- [x] ~~The website is not responsive. Add support for mobile devices. Make the mockup on this [Figma file](https://www.figma.com/file/b6BfADl65XBriu5W1gQGN3/Harvest?node-id=0%3A1)~~ deferred to v0.2
- [x] The Startup Guide is outdated. Update it using the latest Harvest API. 


Discussion / hints:

Fix patch:
diff --git a/examples/crypto.py b/examples/crypto.py
index 5f3c523f..7630979b 100644
--- a/examples/crypto.py
+++ b/examples/crypto.py
@@ -19,6 +19,7 @@ def setup(self):
     def main(self, _):        
         # Get the current time as a datetime object
         self.timestamp = self.get_datetime()
+        print(f"Time: {self.timestamp}")
 
         # Get a list of price history for Dogecoin
         # When a symbol is not specified, it defaults to the first symbol on 
@@ -59,7 +60,7 @@ def main(self, _):
 
         if not self.hold:
             if self.buy_eval(trends, prices, candles):
-                print(f"BUY: {self.timestamp}, {sym}, {prices[-1]}")
+                print(f"BUY: {self.timestamp}, {prices[-1]}")
                 # Get how much Dogecoin we can buy
                 buy_qty = self.get_max_quantity(round=False)
                 self.buy_qty = buy_qty
@@ -75,9 +76,9 @@ def main(self, _):
             # Get how much returns you have made in Dogecoin so far
             ret = self.get_returns()
             if self.sell_eval(ret):
-                print(f"SELL: {self.timestamp}, {sym}, {prices[-1]}")
+                print(f"SELL: {self.timestamp}, {prices[-1]}")
                 # Sell Dogecoin
-                self.sell(sym, self.buy_qty)
+                self.sell(quantity=self.buy_qty)
                 self.hold = False
             else:
                 print(f"HOLD: {ret}")
@@ -99,4 +100,4 @@ def sell_eval(self, ret):
     t.set_symbol('@DOGE')   # Cryptocurrencies are prepended with an '@'
     t.set_algo(Crypto())
     
-    t.start(interval='30MIN') # Run the algorithm once every 30 minutes
+    t.start(interval='1MIN') # Run the algorithm once every 30 minutes
diff --git a/examples/monitor.py b/examples/monitor.py
index a3744b27..365dd461 100644
--- a/examples/monitor.py
+++ b/examples/monitor.py
@@ -11,7 +11,7 @@ def main(self, meta):
 
 if __name__ == "__main__":
     t = Trader( RobinhoodBroker() )
-    t.set_symbol('TWTR')
+    t.set_symbol('@BTC')
     t.set_algo(Watch())
 
-    t.start()
\ No newline at end of file
+    t.start('1MIN')
\ No newline at end of file
diff --git a/harvest/algo.py b/harvest/algo.py
index eb5bfe6e..2347273f 100644
--- a/harvest/algo.py
+++ b/harvest/algo.py
@@ -24,14 +24,6 @@ def setup(self):
 
     def main(self, meta = {}):
         pass
-
-    ############ Functions to configure the algo #################
-
-    def add_symbol(self, symbol: str) -> None:
-        self.watch.append(symbol)
-    
-    def remove_symbol(self, symbol: str) -> None:
-        self.watch.remove(symbol)
     
     ############ Functions interfacing with broker through the trader #################
 
@@ -126,7 +118,7 @@ def await_sell(self, symbol: str=None, quantity: int=0, in_force: str='gtc', ext
     def buy_option(self, symbol: str, quantity: int=None, in_force: str='gtc'):
         """Buys the specified option.
         
-        :param str symbol:    Symbol of the asset to buy, in OCC format. 
+        :param str symbol:    Symbol of the asset to buy, in {OCC} format. 
         :param float? quantity:  Quantity of asset to buy. defaults to buys as many as possible
         :param str? in_force:  Duration the order is in force. '{gtc}' or '{gtd}'. defaults to 'gtc'
         :returns: A dictionary with the following keys:
@@ -144,7 +136,7 @@ def buy_option(self, symbol: str, quantity: int=None, in_force: str='gtc'):
     def sell_option(self, symbol: str, quantity: int=None, in_force: str='gtc'):
         """Sells the specified option.
         
-        :param str? symbol:    Symbol of the asset to sell, in OCC format. 
+        :param str? symbol:    Symbol of the asset to sell, in {OCC} format. 
         :param float? quantity:  Quantity of asset to sell. defaults to sells all
         :param str? in_force:  Duration the order is in force. '{gtc}' or '{gtd}'. defaults to 'gtc'
         :returns: A dictionary with the following keys:
@@ -194,7 +186,7 @@ def get_chain_data(self, symbol: str):
     def get_option_market_data(self, symbol: str):
         """Retrieves data of specified option. 
 
-        :param str? symbol: OCC symbol of option
+        :param str? symbol: {OCC} symbol of option
         :returns: A dictionary:
 
             - price: price of option 
@@ -220,7 +212,7 @@ def default_param(self, symbol, interval, ref, prices):
             if interval == None:
                 interval = self.trader.interval
             if prices == None:
-                prices = self.trader.storage.load(symbol, interval)
+                prices = self.trader.storage.load(symbol, interval)[symbol][ref]
            
         return symbol, interval, ref, prices
 
@@ -452,7 +444,7 @@ def get_candle_list(self, symbol:str=None, interval=None) -> pd.DataFrame():
     def get_returns(self, symbol=None) -> float:
         """Returns the return of a specified asset.
 
-        :param str? symbol:  Symbol of stock, crypto, or option. Options should be in OCC format. 
+        :param str? symbol:  Symbol of stock, crypto, or option. Options should be in {OCC} format. 
                         defaults to first symbol in watchlist
         :returns: Return of asset, expressed as a decimal. 
         """
@@ -466,7 +458,7 @@ def get_returns(self, symbol=None) -> float:
     def get_max_quantity(self, symbol=None, round=True):
         """Calculates the maximum quantity of an asset that can be bought given the current buying power. 
 
-        :param str? symbol:  Symbol of stock, crypto, or option. Options should be in OCC format. 
+        :param str? symbol:  Symbol of stock, crypto, or option. Options should be in {OCC} format. 
                         defaults to first symbol in watchlist
         :param bool? round:  If set to True, the result is returned as an integer. defaults to True 
         :returns: Quantity that can be bought.
@@ -526,6 +518,14 @@ def get_datetime(self):
         :returns: The current date and time as a datetime object
         """
         return self.trader.timestamp 
+    
+    # Used for testing
+    def add_symbol(self, symbol:str):
+        """Adds a symbol to the watchlist. 
+
+        :param str symbol: Symbol of stock or crypto asset. 
+        """
+        self.watch.append(symbol)
 
     
     
diff --git a/harvest/broker/__init__.py b/harvest/api/__init__.py
similarity index 100%
rename from harvest/broker/__init__.py
rename to harvest/api/__init__.py
diff --git a/harvest/broker/_base.py b/harvest/api/_base.py
similarity index 70%
rename from harvest/broker/_base.py
rename to harvest/api/_base.py
index 6e9d1b3c..869059cc 100644
--- a/harvest/broker/_base.py
+++ b/harvest/api/_base.py
@@ -1,72 +1,91 @@
 # Builtins
-import re
 import datetime as dt
 import time
 from logging import debug
-from warnings import warn
 from typing import Any, Callable, Dict, List 
-import re
+from pathlib import Path
+import yaml
+import traceback
 
 # External libraries
-import numpy as np
 import pandas as pd
-import pytz
 
 # Submodule imports
-import harvest.utils as util
 from harvest.utils import *
 
-class BaseBroker:
-    """Broker class communicates with various API endpoints to perform the
-    necessary operations. The BaseBroker defines the interface for all Broker classes to 
+class API:
+    """
+    The API class communicates with various API endpoints to perform the
+    necessary operations. The Base class defines the interface for all API classes to 
     extend and implement.
 
     Attributes
+    :interval_list: A list of supported intervals.
     :fetch_interval: A string indicating the interval the broker fetches the latest asset data.  
         This should be initialized in setup_run (see below).
     """
     
-    def __init__(self, path: str=None):
-        """Here, the broker should perform any authentications necessary to 
-        connect to the brokerage it is using.
+    def __init__(self, path: str):
+        """
+        Here, you should perform any authentications necessary to 
+        communicate with the API this class is using. 
 
-        There are three broker types, 'streamer', 'broker', and 'both'. A 
+        There are three API class types, 'streamer', 'broker', and 'both'. A 
         'streamer' is responsible for fetching data and interacting with 
         the queue to store data. A 'broker' is used solely for buying and 
         selling stocks, cryptos and options. Finally, 'both' is used to 
         indicate that the broker fetch data and buy and sell stocks.
+        
+        All subclass implementations should call this __init__ method
+        using `super().__init__(path)`.
 
-        :path: path to the YAML file containing credentials for the broker. 
-            If not specified, should default to './secret.yaml'
+        :path: path to the YAML file containing credentials to communicate with the API. 
+            If not specified, defaults to './secret.yaml'
         """
-
         self.trader = None # Allows broker to handle the case when runs without a trader
+        
+        if path == None:
+            path = './secret.yaml'
+        # Check if file exists
+        yml_file = Path(path)
+        if not yml_file.is_file():
+            if not self.no_secret(path):
+                return 
+        with open(path, 'r') as stream:
+            self.config = yaml.safe_load(stream)
     
-    def setup(self, watch: List[str], interval: str, fetch_interval:str, trader=None, trader_main=None) -> None:
-        """This function is called right before the algorithm begins.
-        It should perform any configurations necessary to start running.
+    def no_secret(self, path: str): 
+        """
+        This method is called when the yaml file with credentials 
+        is not found. """
+        raise Exception(f"{path} was not found")
+    
+    def refresh_cred(self):
+        """
+        Most API endpoints, for security reasons, require a refresh of the access token
+        every now and then. This method should perform a refresh of the access token. 
+        """
+        pass
 
-        :watch: List of stocks/cryptos to watch. Cryptos are prepended with a '@'
-            to distinguish them from stocks
-        :interval: Interval to call the algo's handler 
-        :fetch_interval: Interval to do fetch stock/crypto data
-        
-        Regardless of the implementation, this method must initialize 
-        the following attributes:
+    def setup(self, watch: List[str], interval: str, fetch_interval:str, trader=None, trader_main=None) -> None:
+        """
+        This function is called right before the algorithm begins.
 
+        On top of performing any configurations and input checks,
+        this method must initialize the following attributes:
         
-        :watch: A list containing strings of stock/crypto (but not option) symbols this broker should 
+        :watch: A list containing strings of stock/crypto (but not option) symbols this class should 
             keep track of. Cryptos are prepended with a '@' to distinguish them from stocks.
         :interval: A string specifying the interval to run the algorithm.
-        :fetch_interval: A string specifying the interval to collect data. This is needed
-            because some brokers like Alpaca Market allow data streaming. Usually data streaming 
-            streams data every minute, so even if the algorithm is designed to run at a lower frequency 
-            (like once every 30MIN), under the hood Harvest needs to process data every minute.
-        :trader: A reference to the parent Trader class
-        :handler: A reference to a method in the Trader class that invokes 
+        :fetch_interval: A string specifying the interval to collect data. 
+            For example, say a broker only provides 1MIN data. If the user wants to 
+            run the algorithm at 5MIN, fetch_interval should be set to '1MIN'.
+            The Trader class will then automatically resample the 1MIN data to 
+            5MIN data.
+        :trader: A reference to the Trader class. 
+        :trader_main: A reference to a method in the Trader class that invokes 
             the algorithm
         """
-        
         self.watch = watch 
         self.interval = interval 
         self.fetch_interval = fetch_interval
@@ -74,61 +93,73 @@ def setup(self, watch: List[str], interval: str, fetch_interval:str, trader=None
         self.trader_main = trader_main
 
     def start(self, kill_switch: bool=False):
-        self.cur_sec = -1
+        """
+        This method begins streaming data from the API.
+
+        The default implementation below is for polling the API.
+        If your brokerage provides a streaming API, you should override
+        this method and configure it to use that API. In that case,
+        make sure to set the callback function to self.main().
+
+        :kill_switch: A flag to indicate whether the algorithm should stop 
+            after a single iteration. Usually used for testing.
+        """
+        self.cur_hr = -1
         self.cur_min = -1
-        val, _ = expand_interval(self.fetch_interval)
+        val, unit = expand_interval(self.fetch_interval)
         
         print("Running...")
         if kill_switch:
             self.main()
             return
-        while 1:
-            cur = dt.datetime.now()
-            minutes = cur.minute
-            if minutes % val == 0 and minutes != self.cur_min:
-                self.main()
-            self.cur_min = minutes
-
-    def refresh_cred(self):
-        pass
-
-    def main_wrap(func):
-        """Wrapper to run the handler async"""
-        def wrapper(*args, **kwargs):
-            self = args[0]
-            df = func(*args, **kwargs) 
-            now = util.now()
-            self.trader.loop.run_until_complete(self.trader_main(df, now))
-        return wrapper
+        
+        if unit == 'MIN':
+            while 1:
+                cur = dt.datetime.now()
+                minutes = cur.minute
+                if minutes % val == 0 and minutes != self.cur_min:
+                    self.main()
+                self.cur_min = minutes
+        elif unit == 'HR':
+            while 1:
+                cur = dt.datetime.now()
+                minutes = cur.minute
+                if minutes == 0 and minutes != self.cur_min:
+                    self.main()
+                self.cur_min = minutes
+        else:
+            raise Exception(f"Unsupported interval {self.fetch_interval}")
 
     def main(self) -> Dict[str, pd.DataFrame]:
-        """This function should be called at the specified interval, and return data.
+        """
+        This function should be called at the specified interval, and return data.
         For brokers that use streaming, this often means specifying this function as a callback.
         For brokers that use polling, this often means calling whatever endpoint is needed to 
         obtain stock/crypto data, at the specified interval.
 
-        :returns: A dictionary where each key is the symbol for an asset, and the value is the
-            corresponding data in the following pandas dataframe format:
+        This method should create a dictionary where each key is the symbol for an asset, 
+        and the value is the corresponding data in the following pandas dataframe format:
                       Symbol                              
                       open   high    low close   volume       
             timestamp
             ---       ---    ---     --- ---     ---         
 
-        timestamp should be an offset-aware datetime object in UTC timezone
+        timestamp should be an offset-aware datetime object in UTC timezone.
+        
+        The dictionary should be passed to the trader by calling `self.trader_main(dict)` 
         """
         raise NotImplementedError("This endpoint is not supported in this broker")
 
     def exit(self):
-        """This function is called after every invocation of algo's handler. 
+        """
+        This function is called after every invocation of algo's handler. 
         The intended purpose is for brokers to clear any cache it may have created.
         """
         pass
 
-    def has_interval(self, interval: str):
-        return interval in self.interval_list
-
     def _exception_handler( func ):
-        """Wrapper to handle unexpected errors in the wrapped function. 
+        """
+        Wrapper to handle unexpected errors in the wrapped function. 
         Most functions should be wrapped with this to properly handle errors, such as
         when internet connection is lost. 
 
@@ -142,34 +173,77 @@ def wrapper(*args, **kwargs):
                     return func(*args, **kwargs) 
                 except Exception as e:
                     debug(f"Error: {e}")
-                    debug("Logging out and back in...")
-                    args[0].refresh_cred()
+                    traceback.print_exc()
+                    # debug("Logging out and back in...")
+                    # args[0].refresh_cred()
                     tries = tries - 1 
                     debug("Retrying...")
                     continue
             raise Exception(f"{func} failed")
         return wrapper
 
+    # -------------- Streamer methods -------------- #
+
     def fetch_price_history(self,
         symbol: str,
         interval: str,
         start: dt.datetime=None, 
         end: dt.datetime=None, 
         ):
-        """Returns historical price data for the specified asset and period 
-        from the API.
-
-        :last: The starting date of the period, inclusive.
-        :today: The ending date of the period, inclusive.
-        :interval: The interval of requested historical data.
-        :symbol: The stock/crypto to get data for.
+        """
+        Fetches historical price data for the specified asset and period 
+        using the API.
+
+        :param symbol: The stock/crypto to get data for.
+        :param interval: The interval of requested historical data.
+        :param start: The starting date of the period, inclusive.
+        :param end: The ending date of the period, inclusive.
+        :returns: A pandas dataframe, same format as main()
+        """
+        raise NotImplementedError("This endpoint is not supported in this broker")
 
-        :returns: A pandas dataframe, same format as _handler()
+    def fetch_chain_info(self, symbol: str):
         """
+        Returns information about the symbol's options
+        
+        :param symbol: Stock symbol. Cannot use crypto.
+        :returns: A dict with the following keys and values:
+            - id: ID of the option chain 
+            - exp_dates: List of expiration dates as datetime objects
+            - multiplier: Multiplier of the option, usually 100 
+        """ 
+    
+    def fetch_chain_data(self, symbol: str):
+        """
+        Returns the option chain for the specified symbol. 
+        
+        :param symbol: Stock symbol. Cannot use crypto.
+        :returns: A dataframe in the following format:
+
+                    exp_date strike  type   
+            OCC
+            ---     ---      ---     ---        
+        exp_date should be a timezone-aware datetime object localized to UTC
+        """ 
         raise NotImplementedError("This endpoint is not supported in this broker")
     
+    def fetch_option_market_data(self, symbol: str):
+        """
+        Retrieves data of specified option. 
+
+        :param symbol:    OCC symbol of option
+        :returns:   A dictionary:
+            - price: price of option 
+            - ask: ask price
+            - bid: bid price
+        """
+        raise NotImplementedError("This endpoint is not supported in this broker") 
+    
+    # ------------- Broker methods ------------- #
+
     def fetch_stock_positions(self):
-        """Returns all current stock positions
+        """
+        Returns all current stock positions
         
         :returns: A list of dictionaries with the following keys and values:
             - symbol: Ticker symbol of the stock
@@ -179,11 +253,12 @@ def fetch_stock_positions(self):
         raise NotImplementedError("This endpoint is not supported in this broker")
 
     def fetch_option_positions(self):
-        """Returns all current option positions
+        """
+        Returns all current option positions
         
         :returns: A list of dictionaries with the following keys and values:
             - symbol: Ticker symbol of the underlying stock
-            - occ_symbol: OCC symbol of option
+            - occ_symbol: OCC symbol of the option
             - avg_price: Average price the option was bought at
             - quantity: Quantity owned
             - multiplier: How many stocks each option represents
@@ -194,7 +269,8 @@ def fetch_option_positions(self):
         raise NotImplementedError("This endpoint is not supported in this broker")
 
     def fetch_crypto_positions(self):
-        """Returns all current crypto positions
+        """
+        Returns all current crypto positions
         
         :returns: A list of dictionaries with the following keys and values:
             - symbol: Ticker symbol for the crypto, prepended with an '@'
@@ -204,8 +280,10 @@ def fetch_crypto_positions(self):
         raise NotImplementedError("This endpoint is not supported in this broker")
     
     def update_option_positions(self, positions: List[Any]):
-        """Updates entries in option_positions list. This is needed as options are priced
-        based on various metrics, and cannot be easily calculated from stock prices. 
+        """
+        Updates entries in option_positions list with the latest option price. 
+        This is needed as options are priced based on various metrics, 
+        and cannot be easily calculated from stock prices. 
 
         :positions: The option_positions list in the Trader class. 
         :returns: Nothing
@@ -213,18 +291,20 @@ def update_option_positions(self, positions: List[Any]):
         raise NotImplementedError("This endpoint is not supported in this broker")
 
     def fetch_account(self):
-        """Returns current account information from the brokerage. 
+        """
+        Returns current account information from the brokerage. 
         
         :returns: A dictionary with the following keys and values:
             - equity: Total assets in the brokerage
             - cash: Total cash in the brokerage
             - buying_power: Total buying power
-            - multiplier: Scale of leverge, if leveraging
+            - multiplier: Scale of leverage, if leveraging
         """
         raise NotImplementedError("This endpoint is not supported in this broker")
     
     def fetch_stock_order_status(self, id):
-        """Returns the status of a stock order with the given id.
+        """
+        Returns the status of a stock order with the given id.
 
         :id: ID of the stock order 
         
@@ -241,7 +321,8 @@ def fetch_stock_order_status(self, id):
         raise NotImplementedError("This endpoint is not supported in this broker")
     
     def fetch_option_order_status(self, id):
-        """Returns the status of a option order with the given id.
+        """
+        Returns the status of a option order with the given id.
 
         :id: ID of the option order 
         
@@ -258,7 +339,8 @@ def fetch_option_order_status(self, id):
         raise NotImplementedError("This endpoint is not supported in this broker")
     
     def fetch_crypto_order_status(self, id):
-        """Returns the status of a crypto order with the given id.
+        """
+        Returns the status of a crypto order with the given id.
 
         :id: ID of the crypto order 
         
@@ -275,7 +357,8 @@ def fetch_crypto_order_status(self, id):
         raise NotImplementedError("This endpoint is not supported in this broker")
     
     def fetch_order_queue(self):
-        """Returns all current pending orders 
+        """
+        Returns all current pending orders 
         
         returns: A list of dictionaries with the following keys and values:
             For stocks:
@@ -310,38 +393,7 @@ def fetch_order_queue(self):
         """
         raise NotImplementedError("This endpoint is not supported in this broker")
 
-    def fetch_chain_info(self, symbol: str):
-        """Returns information about the symbol's options
-        
-        :returns: A dict with the following keys and values:
-            - id: ID of the option chain 
-            - exp_dates: List of expiration dates, in the fomrat "YYYY-MM-DD" 
-            - multiplier: Multiplier of the option, usually 100 
-        """ 
-    
-    def fetch_chain_data(self, symbol: str):
-        """Returns the option chain for the specified symbol. 
-        
-        :symbol: symbol 
-        :returns: A dataframe in the following format:
-
-                    exp_date strike  type    id
-            OCC
-            ---     ---      ---     ---     ---     
-        exp_date should be a timezone-aware date localized to UTC
-        """ 
-        raise NotImplementedError("This endpoint is not supported in this broker")
-    
-    def fetch_option_market_data(self, symbol: str):
-        """Retrieves data of specified option. 
-
-        :symbol:    Occ symbol of option
-        :returns:   A dictionary:
-            - price: price of option 
-            - ask: ask price
-            - bid: bid price
-            }
-        """ 
+    # --------------- Methods for Trading --------------- #
 
     def order_limit(self, 
         side: str, 
@@ -351,7 +403,8 @@ def order_limit(self,
         in_force: str='gtc', 
         extended: bool=False, 
         ):
-        """Places a limit order. 
+        """
+        Places a limit order. 
 
         :symbol:    symbol of asset
         :side:      'buy' or 'sell'
@@ -368,8 +421,34 @@ def order_limit(self,
         """
         raise NotImplementedError("This endpoint is not supported in this broker")
     
+    def order_option_limit(self, side: str, symbol: str, quantity: float, limit_price: float, type: str, 
+        exp_date: dt.datetime, strike: float, in_force: str='gtc'):
+        """
+        Order an option.
+
+        :side:      'buy' or 'sell'
+        :symbol:    symbol of asset
+        :in_force:  
+        :limit_price: limit price
+        :quantity:  quantity to sell or buy
+        :exp_date:  expiration date
+        :strike:    strike price
+        :type:      'call' or 'put'
+
+        :returns: A dictionary with the following keys and values:
+            - type: 'OPTION'
+            - id: ID of order
+            - symbol: symbol of asset
+            Raises an exception if order fails. 
+        """
+        raise NotImplementedError("This endpoint is not supported in this broker")
+
+    # -------------- Built-in methods -------------- #
+    # These do not need to be re-implemented in a subclass
+
     def buy(self, symbol: str=None, quantity: int=0, in_force: str='gtc', extended: bool=False):
-        """Buys the specified asset.
+        """
+        Buys the specified asset.
 
         :symbol:    Symbol of the asset to buy
         :quantity:  Quantity of asset to buy
@@ -385,7 +464,7 @@ def buy(self, symbol: str=None, quantity: int=0, in_force: str='gtc', extended:
 
         if self.trader is None:
             buy_power = self.fetch_account()['buying_power']
-            price = self.fetch_price_history( symbol, self.interval, now() - dt.timedelta(days=7), now())[symbol]['close'][-1]
+            price = self.streamer.fetch_price_history( symbol, self.interval, now() - dt.timedelta(days=7), now())[symbol]['close'][-1]
         else:
             buy_power = self.trader.account['buying_power']
             price = self.trader.queue.get_last_symbol_interval_price(symbol, self.fetch_interval, 'close')
@@ -401,7 +480,8 @@ def buy(self, symbol: str=None, quantity: int=0, in_force: str='gtc', extended:
         return self.order_limit('buy', symbol, quantity, limit_price, in_force, extended)
     
     def await_buy(self, symbol: str=None, quantity: int=0, in_force: str='gtc', extended: bool=False):
-        """Buys the specified asset, and hangs the code until the order is filled. 
+        """
+        Buys the specified asset, and hangs the code until the order is filled. 
 
         :symbol:    Symbol of the asset to buy
         :quantity:  Quantity of asset to buy
@@ -419,9 +499,9 @@ def await_buy(self, symbol: str=None, quantity: int=0, in_force: str='gtc', exte
                 check = self.fetch_stock_order_status
         else:
             if is_crypto(symbol):
-                check = self.trader.order.fetch_crypto_order_status
+                check = self.trader.broker.fetch_crypto_order_status
             else:
-                check = self.trader.order.fetch_stock_order_status
+                check = self.trader.broker.fetch_stock_order_status
 
         while True:
             time.sleep(0.5)
@@ -441,18 +521,11 @@ def sell(self, symbol: str=None, quantity: int=0, in_force: str='gtc', extended:
         """
         if symbol == None:
             symbol = self.watch[0]
-        
-        # if isinstance(quantity, str):
-        #     for p in self.trader.stock_positions + self.trader.crypto_positions:
-        #         if p['symbol'] == symbol:
-        #             quantity = p['quantity']
-        #             break
         if quantity <= 0.0:
             raise Exception(f"Quantity cannot be less than or equal to 0: was given {quantity}")
-            return None
        
         if self.trader is None:
-            price = self.fetch_price_history(symbol, self.interval, now() - dt.timedelta(days=7), now())[symbol]['close'][-1]
+            price = self.streamer.fetch_price_history(symbol, self.interval, now() - dt.timedelta(days=7), now())[symbol]['close'][-1]
         else:
             price = self.trader.queue.get_last_symbol_interval_price(symbol, self.fetch_interval, 'close') 
 
@@ -461,7 +534,8 @@ def sell(self, symbol: str=None, quantity: int=0, in_force: str='gtc', extended:
         return self.order_limit('sell', symbol, quantity, limit_price, in_force, extended)
 
     def await_sell(self, symbol: str=None, quantity: int=0, in_force: str='gtc', extended: bool=False):
-        """Sells the specified asset, and hangs the code until the order is filled. 
+        """
+        Sells the specified asset, and hangs the code until the order is filled. 
 
         :symbol:    Symbol of the asset to buy
         :quantity:  Quantity of asset to buy
@@ -477,34 +551,14 @@ def await_sell(self, symbol: str=None, quantity: int=0, in_force: str='gtc', ext
             if self.trader is None:
                 stat = self.fetch_stock_order_status(ret["id"])
             else:
-                stat = self.trader.order.fetch_stock_order_status(ret["id"])
+                stat = self.trader.broker.fetch_stock_order_status(ret["id"])
             if stat['status'] == 'filled':
                 return stat
             time.sleep(1)
        
-    def order_option_limit(self, side: str, symbol: str, quantity: float, limit_price: float, type: str, 
-        exp_date: dt.datetime, strike: float, in_force: str='gtc'):
-        """Order an option.
-
-        :side:      'buy' or 'sell'
-        :symbol:    symbol of asset
-        :in_force:  
-        :limit_price: limit price
-        :quantity:  quantity to sell or buy
-        :exp_date:  expiration date
-        :strike:    strike price
-        :type:      'call' or 'put'
-
-        :returns: A dictionary with the following keys and values:
-            - type: 'OPTION'
-            - id: ID of order
-            - symbol: symbol of asset
-            Raises an exception if order fails. 
-        """
-        raise NotImplementedError("This endpoint is not supported in this broker")
-    
     def buy_option(self, symbol: str=None, quantity: int=0, in_force: str='gtc'):
-        """Buys the specified option.
+        """
+        Buys the specified option.
         
         :symbol:    Symbol of the asset to buy, in OCC format. 
         :quantity:  Quantity of asset to buy
@@ -519,7 +573,7 @@ def buy_option(self, symbol: str=None, quantity: int=0, in_force: str='gtc'):
 
         if self.trader is None:
             buy_power = self.fetch_account()['buying_power']
-            price = self.fetch_option_market_data(symbol)['price']
+            price = self.streamer.fetch_option_market_data(symbol)['price']
         else:
             buy_power = self.trader.account['buying_power']
             price = self.trader.streamer.fetch_option_market_data(symbol)['price']
@@ -536,7 +590,8 @@ def buy_option(self, symbol: str=None, quantity: int=0, in_force: str='gtc'):
         return self.order_option_limit('buy', sym, quantity, limit_price, option_type, date, strike, in_force=in_force)
 
     def sell_option(self, symbol: str=None, quantity: int=0, in_force: str='gtc'):
-        """Sells the sepcified option.
+        """
+        Sells the specified option.
         
         :symbol:    Symbol of the asset to buy, in OCC format. 
         :quantity:  Quantity of asset to buy
@@ -548,14 +603,9 @@ def sell_option(self, symbol: str=None, quantity: int=0, in_force: str='gtc'):
             raise Exception("Option symbol was not specified")
         if quantity <= 0.0:
             raise Exception(f"Quantity cannot be less than or equal to 0: was given {quantity}")
-        # if isinstance(quantity, str):
-        #     for p in self.trader.stock_positions + self.trader.crypto_positions + self.trader.option_positions:
-        #         if p['symbol'] == symbol:
-        #             quantity = p['quantity']
-        #             break
        
         if self.trader is None:
-            price = self.fetch_option_market_data(symbol)['price']
+            price = self.streamer.fetch_option_market_data(symbol)['price']
         else:
             price = self.trader.streamer.fetch_option_market_data(symbol)['price']
             
@@ -564,16 +614,19 @@ def sell_option(self, symbol: str=None, quantity: int=0, in_force: str='gtc'):
         sym, date, option_type, strike = self.occ_to_data(symbol)
         return self.order_option_limit('sell', sym, quantity, limit_price, option_type, date, strike, in_force=in_force)
 
-    ############# Helper functions #################
+    # -------------- Helper methods -------------- #
+    
+    def has_interval(self, interval: str):
+        return interval in self.interval_list
 
     def data_to_occ(self, symbol: str, date: dt.datetime, option_type: str, price: float):
-        """Converts data into a OCC format string 
+        """
+        Converts data into a OCC format string 
         """
         occ = symbol+((6-len(symbol))*' ')
         occ = occ+date.strftime('%y%m%d')
         occ = occ+'C' if option_type == 'call' else occ+'P'
         occ = occ+f'{int(price*1000):08}'
-
         return occ
     
     def occ_to_data(self, symbol: str):
diff --git a/harvest/api/dummy.py b/harvest/api/dummy.py
new file mode 100644
index 00000000..3db761fd
--- /dev/null
+++ b/harvest/api/dummy.py
@@ -0,0 +1,187 @@
+# Builtins
+import datetime as dt
+import random
+from typing import Any, Dict, List, Tuple
+from logging import critical, error, info, warning, debug
+
+# External libraries
+import pandas as pd
+
+# Submodule imports
+from harvest.api._base import API
+from harvest.utils import *
+
+class DummyStreamer(API):
+    """DummyStreamer, as its name implies, is a dummy broker class that can 
+    be useful for testing algorithms. When used as a streamer, it will return
+    randomly generated prices. 
+    """
+
+    interval_list = ['1MIN', '5MIN', '15MIN', '30MIN', '1HR', '1DAY']
+
+    def __init__(self, path: str=None):
+        self.trader = None
+
+    def setup(self, watch: List[str], interval, trader=None, trader_main=None):
+        super().setup(watch, interval, interval, trader, trader_main)
+
+    def main(self):
+        df_dict = {}
+        df_dict.update(self.fetch_latest_stock_price())
+        df_dict.update(self.fetch_latest_crypto_price())
+      
+        self.trader_main(df_dict)
+    
+    def fetch_latest_stock_price(self) -> Dict[str, pd.DataFrame]:
+        results = {}
+        today = dt.datetime.now()
+        last = today - dt.timedelta(days=7)
+        
+        for symbol in self.watch:
+            if not is_crypto(symbol):
+                results[symbol] = self.fetch_price_history(symbol, self.interval, last, today).iloc[[-1]]
+        return results
+        
+    def fetch_latest_crypto_price(self) -> Dict[str, pd.DataFrame]:
+        results = {}
+        today = dt.datetime.now()
+        last = today - dt.timedelta(days=7)
+        for symbol in self.watch:
+            if is_crypto(symbol):
+                results[symbol] = self.fetch_price_history(symbol, self.interval, last, today).iloc[[-1]]
+        return results
+
+    def _generate_fake_stock_data(self):
+        open_s = random.uniform(2, 1000)
+        volume = random.randint(1, 1e7)
+
+        while True: 
+            open_s = max(open_s + random.uniform(-1, 1), 0.001)
+            close = open_s + random.uniform(-1, 1)
+            low = max(min(open_s, close) - random.uniform(0.001, 1), 0)
+            high = max(open_s, close) + random.uniform(0.001, 1)
+            volume = max(volume + random.randint(-5, 5), 1)  
+            yield open_s, high, low, close, volume
+
+    # -------------- Streamer methods -------------- #
+
+    def fetch_price_history(self,
+        symbol: str,
+        interval: str,
+        start: dt.datetime=None, 
+        end: dt.datetime=None
+        ) -> pd.DataFrame:
+
+        if start is None:  
+            if interval in ['1MIN', '5MIN', '15MIN', '30MIN']:
+                start = now() - dt.timedelta(days=7)
+            elif interval == '1HR':
+                start = now() - dt.timedelta(days=31)
+            else:
+                start = now() - dt.timedelta(days=365)
+        if end is None:
+            end = now()
+
+        value, unit = expand_interval(interval)
+        if unit == 'MIN':
+            interval = dt.timedelta(minutes=int(value))
+        elif unit == 'HR':
+            interval = dt.timedelta(hours=int(value))
+        elif unit == 'DAY':
+            interval = dt.timedelta(days=int(value))
+        else:
+            print('Error')
+
+        times = []
+        current = start
+
+        stock_gen = self._generate_fake_stock_data()
+        open_s = []
+        high = []
+        low = []
+        close = []
+        volume = []
+
+        # Fake the data 
+        while current < end + interval:
+            times.append(current.replace(tzinfo=None))
+            current += interval
+
+            o, h, l, c, v = next(stock_gen)
+            open_s.append(o)
+            high.append(h)
+            low.append(l)
+            close.append(c)
+            volume.append(v)
+
+        if not symbol:
+            symbol = 'DUMMY'            
+
+        d = {
+            'timestamp': times,
+            'open': open_s,
+            'high': high,
+            'low': low, 
+            'close': close,
+            'volume': volume
+        }
+
+        results = pd.DataFrame(data=d).set_index('timestamp')
+        open_time = dt.time(hour=13, minute=30)
+        close_time = dt.time(hour=20)
+
+        results.index = times
+        results.index.rename('timestamp', inplace=True)
+
+        results = results.loc[(open_time < results.index.time) & (results.index.time < close_time)]
+        results = results[(results.index.dayofweek != 5) & (results.index.dayofweek != 6)]
+
+        results.columns = pd.MultiIndex.from_product([[symbol], results.columns])
+
+        return results
+    
+    # TODO: Generate dummy option data
+    def fetch_chain_info(self, symbol: str):
+        raise Exception("Not implemented")
+    
+    def fetch_chain_data(self, symbol: str):
+        raise Exception("Not implemented")
+    
+    def fetch_option_market_data(self, symbol: str):
+        # This is a placeholder so Trader doesn't crash
+        price = random.uniform(2, 1000)
+        return {
+            'price': price,
+            'ask': price, 
+            'bid': price,
+        }
+
+    # ------------- Broker methods ------------- #
+
+    def fetch_stock_positions(self) -> List[Dict[str, Any]]:
+        raise Exception("Not implemented")
+
+    def fetch_option_positions(self) -> List[Dict[str, Any]]:
+        raise Exception("Not implemented")
+
+    def fetch_crypto_positions(self) -> List[Dict[str, Any]]:
+        raise Exception("Not implemented")
+    
+    def update_option_positions(self, positions) -> List[Dict[str, Any]]:
+        raise Exception("Not implemented")
+
+    def fetch_account(self) -> Dict[str, Any]:
+        raise Exception("Not implemented")
+    
+    def fetch_stock_order_status(self, id: int) -> Dict[str, Any]:
+        raise Exception("Not implemented")
+
+    def fetch_option_order_status(self, id: int) -> Dict[str, Any]:
+        raise Exception("Not implemented")
+
+    def fetch_crypto_order_status(self, id: int) -> Dict[str, Any]:
+        raise Exception("Not implemented")
+    
+    def fetch_order_queue(self) -> List[Dict[str, Any]]:
+        raise Exception("Not implemented")
+
diff --git a/harvest/broker/dummy.py b/harvest/api/paper.py
similarity index 68%
rename from harvest/broker/dummy.py
rename to harvest/api/paper.py
index 9f90ac3f..25908e22 100644
--- a/harvest/broker/dummy.py
+++ b/harvest/api/paper.py
@@ -1,22 +1,17 @@
 # Builtins
 import datetime as dt
-import random
-import re
-import queue
 from typing import Any, Dict, List, Tuple
-import logging
 from logging import critical, error, info, warning, debug
 
 # External libraries
 import pandas as pd
 import yaml
-import pytz
 
 # Submodule imports
-import harvest.broker._base as base
+from harvest.api._base import API
 from harvest.utils import *
 
-class DummyBroker(base.BaseBroker):
+class PaperBroker(API):
     """DummyBroker, as its name implies, is a dummy broker class that can 
     be useful for testing algorithms. When used as a streamer, it will return
     randomly generated prices. When used as a broker, it paper trades.
@@ -25,16 +20,15 @@ class DummyBroker(base.BaseBroker):
     interval_list = ['1MIN', '5MIN', '15MIN', '30MIN', '1DAY']
 
     def __init__(self, account_path: str=None):
-        super().__init__()
-
+        
         self.stocks = []
         self.options = []
         self.cryptos = []
         self.orders = []
 
-        self.equity = 100000.0
-        self.cash = 100000.0
-        self.buying_power = 100000.0
+        self.equity = 1000000.0
+        self.cash = 1000000.0
+        self.buying_power = 1000000.0
         self.multiplier = 1
         self.id = 0
 
@@ -54,34 +48,9 @@ def __init__(self, account_path: str=None):
 
 
     def setup(self, watch: List[str], interval, trader=None, trader_main=None):
-        if interval not in self.interval_list:
-            raise Exception(f'Invalid interval {interval}')
-
         super().setup(watch, interval, interval, trader, trader_main)
 
-    @base.BaseBroker.main_wrap
-    def main(self):
-        df_dict = {}
-        df_dict.update(self.fetch_latest_stock_price())
-        df_dict.update(self.fetch_latest_crypto_price())
-      
-        return df_dict
-
-    def has_interval(self, interval: str):
-        return True
-
-    def _generate_fake_stock_data(self):
-        open_s = random.uniform(2, 1000)
-        volume = random.randint(1, 1e7)
-
-        while True: 
-            open_s = max(open_s + random.uniform(-1, 1), 0.001)
-            close = open_s + random.uniform(-1, 1)
-            low = max(min(open_s, close) - random.uniform(0.001, 1), 0)
-            high = max(open_s, close) + random.uniform(0.001, 1)
-            volume = max(volume + random.randint(-5, 5), 1)  
-            yield open_s, high, low, close, volume
-
+    # -------------- Streamer methods -------------- #
 
     def fetch_price_history(self,
         symbol: str,
@@ -89,94 +58,19 @@ def fetch_price_history(self,
         start: dt.datetime=None, 
         end: dt.datetime=None
         ) -> pd.DataFrame:
-
-        if start is None:  
-            if interval in ['1MIN', '5MIN', '15MIN', '30MIN']:
-                start = now() - dt.timedelta(days=7)
-            elif interval == '1HR':
-                start = now() - dt.timedelta(days=31)
-            else:
-                start = now() - dt.timedelta(days=365)
-        if end is None:
-            end = now()
-
-        value, unit = expand_interval(interval)
-        if unit == 'MIN':
-            interval = dt.timedelta(minutes=int(value))
-        elif unit == 'HR':
-            interval = dt.timedelta(hours=int(value))
-        elif unit == 'DAY':
-            interval = dt.timedelta(days=int(value))
-        else:
-            print('Error')
-
-        times = []
-        current = start
-
-        stock_gen = self._generate_fake_stock_data()
-        open_s = []
-        high = []
-        low = []
-        close = []
-        volume = []
-
-        # Fake the data 
-        while current < end + interval:
-            times.append(current.replace(tzinfo=None))
-            current += interval
-
-            o, h, l, c, v = next(stock_gen)
-            open_s.append(o)
-            high.append(h)
-            low.append(l)
-            close.append(c)
-            volume.append(v)
-
-        if not symbol:
-            symbol = 'DUMMY'            
-
-        d = {
-            'timestamp': times,
-            'open': open_s,
-            'high': high,
-            'low': low, 
-            'close': close,
-            'volume': volume
-        }
-
-        results = pd.DataFrame(data=d).set_index('timestamp')
-        open_time = dt.time(hour=13, minute=30)
-        close_time = dt.time(hour=20)
-
-        results.index = times
-        results.index.rename('timestamp', inplace=True)
-
-        results = results.loc[(open_time < results.index.time) & (results.index.time < close_time)]
-        results = results[(results.index.dayofweek != 5) & (results.index.dayofweek != 6)]
-
-        results.columns = pd.MultiIndex.from_product([[symbol], results.columns])
-
-        return results
-
-    def fetch_latest_stock_price(self) -> Dict[str, pd.DataFrame]:
-        results = {}
-        today = dt.datetime.now()
-        last = today - dt.timedelta(days=7)
-        
-        for symbol in self.watch:
-            if not is_crypto(symbol):
-                results[symbol] = self.fetch_price_history(symbol, self.interval, last, today).iloc[[-1]]
-        return results
-        
-    def fetch_latest_crypto_price(self) -> Dict[str, pd.DataFrame]:
-        results = {}
-        today = dt.datetime.now()
-        last = today - dt.timedelta(days=7)
-        for symbol in self.watch:
-            if is_crypto(symbol):
-                results[symbol] = self.fetch_price_history(symbol, self.interval, last, today).iloc[[-1]]
-        return results
+        raise Exception("Not implemented")
     
+    def fetch_chain_info(self, symbol: str):
+        raise Exception("Not implemented")
+    
+    def fetch_chain_data(self, symbol: str):
+        raise Exception("Not implemented")
+    
+    def fetch_option_market_data(self, symbol: str):
+        raise Exception("Not implemented")
+    
+    # ------------- Broker methods ------------- #
+
     def fetch_stock_positions(self) -> List[Dict[str, Any]]:
         return self.stocks
 
@@ -213,7 +107,7 @@ def fetch_stock_order_status(self, id: int) -> Dict[str, Any]:
         sym = ret['symbol']
 
         if self.trader is None:
-            price = self.fetch_price_history(sym, self.interval, dt.datetime.now() - dt.timedelta(days=7), dt.datetime.now())[sym]['close'][-1]
+            price = self.streamer.fetch_price_history(sym, self.interval, dt.datetime.now() - dt.timedelta(days=7), dt.datetime.now())[sym]['close'][-1]
         else:
             price = self.trader.queue.get_last_symbol_interval_price(sym, self.interval, 'close')
 
@@ -279,7 +173,7 @@ def fetch_option_order_status(self, id: int) -> Dict[str, Any]:
         occ_sym = ret['occ_symbol']
 
         if self.trader is None:
-            price = self.fetch_option_market_data(occ_sym)['price']
+            price = self.streamer.fetch_option_market_data(occ_sym)['price']
         else:
             price = self.trader.streamer.fetch_option_market_data(occ_sym)['price']
             
@@ -336,23 +230,8 @@ def fetch_crypto_order_status(self, id: int) -> Dict[str, Any]:
     
     def fetch_order_queue(self) -> List[Dict[str, Any]]:
         return self.orders
-
-    def fetch_chain_info(self, symbol: str):
-        pass
-    
-    def fetch_chain_data(self, symbol: str):
-        pass
-    
-    def fetch_option_market_data(self, symbol: str):
-        price = random.uniform(2, 1000)
-
-        return {
-            'price': price,
-            'ask': price, 
-            'bid': price,
-        }
     
-    ########## Function for order operations ########### 
+    # --------------- Methods for Trading --------------- #
 
     def order_limit(self, 
         side: str, 
diff --git a/harvest/broker/robinhood.py b/harvest/api/robinhood.py
similarity index 90%
rename from harvest/broker/robinhood.py
rename to harvest/api/robinhood.py
index 455da371..e12090b7 100644
--- a/harvest/broker/robinhood.py
+++ b/harvest/api/robinhood.py
@@ -1,44 +1,25 @@
 # Builtins
 import datetime as dt
-from datetime import timedelta
-import logging
-import re
-import threading
 from logging import critical, error, info, warning, debug
 from typing import Any, Dict, List, Tuple
-import time
-from pathlib import Path
 from getpass import getpass
 
 # External libraries
-import dateutil.parser as parser
 import pandas as pd
 import pyotp
 import robin_stocks.robinhood as rh
-import yaml
 import pytz 
 
 # Submodule imports
-import harvest.broker._base as base
+from harvest.api._base import API
 from harvest.utils import *
 
-class RobinhoodBroker(base.BaseBroker):
+class Robinhood(API):
 
     interval_list = ['1MIN', '5MIN', '15MIN', '30MIN', '1DAY']
 
     def __init__(self, path=None):
-        super().__init__()
-
-        if path == None:
-            path = './secret.yaml'
-        # Check if file exists
-        yml_file = Path(path)
-        if not yml_file.is_file():
-            if not self._create_secret(path):
-                return 
-        with open(path, 'r') as stream:
-            self.config = yaml.safe_load(stream)
-    
+        super().__init__(path)
         self.login()
 
     def login(self):
@@ -55,7 +36,7 @@ def refresh_cred(self):
         self.login()
         debug("Logged into Robinhood...")
     
-    def _create_secret(self, path):
+    def no_secret(self, path):
 
         print("""⚠️  Hmm, looks like you haven't set up login credentials for Robinhood yet.""")
 
@@ -133,15 +114,23 @@ def _create_secret(self, path):
         return True 
 
     def setup(self, watch: List[str], interval, trader=None, trader_main=None):
+       
+        if interval == '1MIN':
+            self.interval_fmt = '15second'
+            fetch_interval = '1MIN'
+        else:
+            self.interval_fmt = '5minute'
+            fetch_interval = '5MIN'
+        
+        super().setup(watch, interval, fetch_interval, trader, trader_main)
+
         self.watch_stock = []
         self.watch_crypto = []
         self.watch_crypto_fmt = []
-
         if interval not in self.interval_list:
             raise Exception(f'Invalid interval {interval}')
-
         for s in watch:
-            if s[0] == '@':
+            if is_crypto(s):
                 self.watch_crypto_fmt.append(s[1:])
                 self.watch_crypto.append(s)
             else:
@@ -149,58 +138,53 @@ def setup(self, watch: List[str], interval, trader=None, trader_main=None):
         if len(self.watch_stock) > 0 and interval == '1MIN':
             raise Exception(f'Interval {interval} is only supported for crypto')
         
-        if interval == '1MIN':
-            self.interval_fmt = '15second'
-            fetch_interval = '1MIN'
-        else:
-            self.interval_fmt = '5minute'
-            fetch_interval = '5MIN'
-        
         self.option_cache = {}
 
-        super().setup(watch, interval, fetch_interval, trader, trader_main)
-         
-
-    def start(self, kill_switch: bool=False):
-        self.cur_sec = -1
-        self.cur_min = -1
-        val = int(re.sub("[^0-9]", "", self.fetch_interval))
-        # RH does not support per minute intervals, so instead 15second intervals are used
-        # Note that 1MIN is only supported for crypto
-        
-        print("Running...")
-        if self.interval == '1MIN':
-            while 1:
-                cur = dt.datetime.now()
-                seconds = cur.second
-                if seconds == 1 and seconds != self.cur_sec:
-                    self.main()
-                self.cur_sec = seconds
-                if kill_switch:
-                    return
-        else:
-            while 1:
-                cur = dt.datetime.now()
-                minutes = cur.minute
-                if minutes % val == 0 and minutes != self.cur_min:
-                    self.main()
-                self.cur_min = minutes
-                if kill_switch:
-                    return
-        
-
     def exit(self):
         self.option_cache = {}
 
-    @base.BaseBroker.main_wrap
     def main(self):
         df_dict = {}
         df_dict.update(self.fetch_latest_stock_price())
         df_dict.update(self.fetch_latest_crypto_price())
       
-        return df_dict
+        self.trader_main(df_dict)
     
-    @base.BaseBroker._exception_handler
+    @API._exception_handler
+    def fetch_latest_stock_price(self):
+        df={}
+        for s in self.watch_stock:
+            ret = rh.get_stock_historicals(
+                s,
+                interval=self.interval_fmt, 
+                span='day', 
+                )
+            if 'error' in ret or ret == None or (type(ret) == list and len(ret) == 0):
+                continue
+            df_tmp = pd.DataFrame.from_dict(ret)
+            df_tmp = self._format_df(df_tmp, [s], self.interval).iloc[[-1]]
+            df[s] = df_tmp
+        
+        return df        
+
+    @API._exception_handler
+    def fetch_latest_crypto_price(self):
+        df={}
+        for s in self.watch_crypto_fmt:
+            ret = rh.get_crypto_historicals(
+                s, 
+                interval=self.interval_fmt, 
+                span='hour',
+                )
+            df_tmp = pd.DataFrame.from_dict(ret)
+            df_tmp = self._format_df(df_tmp, ['@'+s], self.interval).iloc[[-1]]
+            df['@'+s] = df_tmp
+        
+        return df       
+
+    # -------------- Streamer methods -------------- #
+    
+    @API._exception_handler
     def fetch_price_history( self,  
         symbol: str,
         interval: str,
@@ -272,40 +256,67 @@ def fetch_price_history( self,
         df = self._format_df(df, [symbol], interval)
         df = aggregate_df(df, interval)
         return df
+    
+    @API._exception_handler
+    def fetch_chain_info(self, symbol: str):
+        ret = rh.get_chains(symbol)
+        return {
+            "id": ret["id"], 
+            "exp_dates": ret["expiration_dates"],
+            "multiplier": ret["trade_value_multiplier"], 
+        }    
 
-    @base.BaseBroker._exception_handler
-    def fetch_latest_stock_price(self):
-        df={}
-        for s in self.watch_stock:
-            ret = rh.get_stock_historicals(
-                s,
-                interval=self.interval_fmt, 
-                span='day', 
-                )
-            if 'error' in ret or ret == None or (type(ret) == list and len(ret) == 0):
-                continue
-            df_tmp = pd.DataFrame.from_dict(ret)
-            df_tmp = self._format_df(df_tmp, [s], self.interval).iloc[[-1]]
-            df[s] = df_tmp
-        
-        return df        
+    @API._exception_handler
+    def fetch_chain_data(self, symbol: str):
 
-    @base.BaseBroker._exception_handler
-    def fetch_latest_crypto_price(self):
-        df={}
-        for s in self.watch_crypto_fmt:
-            ret = rh.get_crypto_historicals(
-                s, 
-                interval=self.interval_fmt, 
-                span='hour',
-                )
-            df_tmp = pd.DataFrame.from_dict(ret)
-            df_tmp = self._format_df(df_tmp, ['@'+s], self.interval).iloc[[-1]]
-            df['@'+s] = df_tmp
+        if bool(self.option_cache) and symbol in self.option_cache:
+            return self.option_cache[symbol]
         
-        return df       
+        ret = rh.find_tradable_options(symbol)
+        exp_date = []
+        strike = []
+        type = []
+        id = []
+        occ = []
+        for entry in ret:
+            date = entry["expiration_date"]
+            date = dt.datetime.strptime(date, '%Y-%m-%d')
+            date = pytz.utc.localize(date)
+            exp_date.append(date)
+            price = float(entry["strike_price"])
+            strike.append(price)
+            type.append(entry["type"])
+            id.append(entry["id"])    
+
+            occ.append(self.data_to_occ(symbol, date, type[-1], price))
+
+        df = pd.DataFrame({'occ_symbol':occ,'exp_date':exp_date,'strike':strike,'type':type,'id':id})
+        df = df.set_index('occ_symbol')
+
+        self.option_cache[symbol] = df
+        return df
+    
+    @API._exception_handler
+    def fetch_option_market_data(self, symbol: str):
+      
+        sym, date, type, price = self.occ_to_data(symbol)
+        ret = rh.get_option_market_data(
+            sym,
+            date.strftime('%Y-%m-%d'),
+            str(price),
+            type
+        )
+        ret = ret[0][0]
+        return {
+                'price': float(ret['adjusted_mark_price']),
+                'ask': float(ret['ask_price']),
+                'bid': float(ret['bid_price'])
+            }
+
+    
+    # ------------- Broker methods ------------- #
 
-    @base.BaseBroker._exception_handler
+    @API._exception_handler
     def fetch_stock_positions(self):
         ret = rh.get_open_stock_positions()
         pos = []
@@ -323,7 +334,7 @@ def fetch_stock_positions(self):
             ) 
         return pos
 
-    @base.BaseBroker._exception_handler
+    @API._exception_handler
     def fetch_option_positions(self):
         ret = rh.get_open_option_positions()
         pos = []
@@ -348,7 +359,7 @@ def fetch_option_positions(self):
     
         return pos 
     
-    @base.BaseBroker._exception_handler
+    @API._exception_handler
     def fetch_crypto_positions(self, key=None):
         ret = rh.get_crypto_positions()
         pos = []
@@ -370,7 +381,7 @@ def fetch_crypto_positions(self, key=None):
             )
         return pos 
     
-    @base.BaseBroker._exception_handler
+    @API._exception_handler
     def update_option_positions(self, positions: List[Any]):
         for r in positions:
             sym, date, type, price = self.occ_to_data(r['occ_symbol'])
@@ -385,7 +396,7 @@ def update_option_positions(self, positions: List[Any]):
             r["market_value"] = float(upd['adjusted_mark_price']) * r['quantity']
             r["cost_basis"] = r['avg_price'] * r['quantity']
 
-    @base.BaseBroker._exception_handler
+    @API._exception_handler
     def fetch_account(self):
         ret = rh.load_phoenix_account()
         ret = {
@@ -396,7 +407,7 @@ def fetch_account(self):
         }
         return ret
 
-    @base.BaseBroker._exception_handler
+    @API._exception_handler
     def fetch_stock_order_status(self, id):
         ret = rh.get_stock_order_info(id)
         return {
@@ -410,7 +421,7 @@ def fetch_stock_order_status(self, id):
             "status":ret['status'],
         }
     
-    @base.BaseBroker._exception_handler
+    @API._exception_handler
     def fetch_option_order_status(self, id):
         ret = rh.get_option_order_info(id)
         return {
@@ -424,7 +435,7 @@ def fetch_option_order_status(self, id):
             "status":ret['state'],
         }
     
-    @base.BaseBroker._exception_handler
+    @API._exception_handler
     def fetch_crypto_order_status(self, id):
         ret = rh.get_crypto_order_info(id)
         return {
@@ -439,7 +450,7 @@ def fetch_crypto_order_status(self, id):
             "status":ret['state'],
         }
     
-    @base.BaseBroker._exception_handler
+    @API._exception_handler
     def fetch_order_queue(self):
         queue = []
         ret = rh.get_all_open_stock_orders()
@@ -493,62 +504,6 @@ def fetch_order_queue(self):
             } )
         return queue
     
-    @base.BaseBroker._exception_handler
-    def fetch_chain_info(self, symbol: str):
-        ret = rh.get_chains(symbol)
-        return {
-            "id": ret["id"], 
-            "exp_dates": ret["expiration_dates"],
-            "multiplier": ret["trade_value_multiplier"], 
-        }    
-
-    @base.BaseBroker._exception_handler
-    def fetch_chain_data(self, symbol: str):
-
-        if bool(self.option_cache) and symbol in self.option_cache:
-            return self.option_cache[symbol]
-        
-        ret = rh.find_tradable_options(symbol)
-        exp_date = []
-        strike = []
-        type = []
-        id = []
-        occ = []
-        for entry in ret:
-            date = entry["expiration_date"]
-            date = dt.datetime.strptime(date, '%Y-%m-%d')
-            date = pytz.utc.localize(date)
-            exp_date.append(date)
-            price = float(entry["strike_price"])
-            strike.append(price)
-            type.append(entry["type"])
-            id.append(entry["id"])    
-
-            occ.append(self.data_to_occ(symbol, date, type[-1], price))
-
-        df = pd.DataFrame({'occ_symbol':occ,'exp_date':exp_date,'strike':strike,'type':type,'id':id})
-        df = df.set_index('occ_symbol')
-
-        self.option_cache[symbol] = df
-        return df
-    
-    @base.BaseBroker._exception_handler
-    def fetch_option_market_data(self, symbol: str):
-      
-        sym, date, type, price = self.occ_to_data(symbol)
-        ret = rh.get_option_market_data(
-            sym,
-            date.strftime('%Y-%m-%d'),
-            str(price),
-            type
-        )
-        ret = ret[0][0]
-        return {
-                'price': float(ret['adjusted_mark_price']),
-                'ask': float(ret['ask_price']),
-                'bid': float(ret['bid_price'])
-            }
-
     # Order functions are not wrapped in the exception handler to prevent duplicate 
     # orders from being made. 
     def order_limit(self, 
diff --git a/harvest/broker/yahoo.py b/harvest/api/yahoo.py
similarity index 89%
rename from harvest/broker/yahoo.py
rename to harvest/api/yahoo.py
index 045a59a2..db00b0b4 100644
--- a/harvest/broker/yahoo.py
+++ b/harvest/api/yahoo.py
@@ -2,17 +2,16 @@
 import datetime as dt
 from logging import critical, error, info, warning, debug
 from typing import Any, Dict, List, Tuple
-import pytz
 
 # External libraries
 import pandas as pd
 import yfinance as yf
 
 # Submodule imports
-import harvest.broker._base as base
+from harvest.api._base import API
 from harvest.utils import *
 
-class YahooStreamer(base.BaseBroker):
+class YahooStreamer(API):
 
     interval_list = ['1MIN', '5MIN', '15MIN', '30MIN', '1HR']
 
@@ -68,7 +67,6 @@ def start(self, kill_switch: bool=False):
     def exit(self):
         self.option_cache = {}
 
-    @base.BaseBroker.main_wrap
     def main(self):
         df_dict = {}
         for s in self.watch_stock:
@@ -76,9 +74,11 @@ def main(self):
             df = df.iloc[[-1]]
             df = self._format_df(df, s)
             df_dict[s] = df
-        return df_dict
+        self.trader_main(df_dict)
+
+    # -------------- Streamer methods -------------- #
     
-    @base.BaseBroker._exception_handler
+    @API._exception_handler
     def fetch_price_history( self,  
         symbol: str,
         interval: str,
@@ -119,44 +119,8 @@ def fetch_price_history( self,
         df = df.loc[start:end]
         
         return df
-
-    @base.BaseBroker._exception_handler
-    def fetch_stock_positions(self):
-        raise Exception("Not implemented")
-
-    @base.BaseBroker._exception_handler
-    def fetch_option_positions(self):
-        raise Exception("Not implemented")
     
-    @base.BaseBroker._exception_handler
-    def fetch_crypto_positions(self, key=None):
-        raise Exception("Not implemented")
-    
-    @base.BaseBroker._exception_handler
-    def update_option_positions(self, positions: List[Any]):
-        raise Exception("Not implemented")
-
-    @base.BaseBroker._exception_handler
-    def fetch_account(self):
-        raise Exception("Not implemented")
-
-    @base.BaseBroker._exception_handler
-    def fetch_stock_order_status(self, id):
-        raise Exception("Not implemented")
-    
-    @base.BaseBroker._exception_handler
-    def fetch_option_order_status(self, id):
-        raise Exception("Not implemented")
-    
-    @base.BaseBroker._exception_handler
-    def fetch_crypto_order_status(self, id):
-        raise Exception("Not implemented")
-    
-    @base.BaseBroker._exception_handler
-    def fetch_order_queue(self):
-        raise Exception("Not implemented")
-    
-    @base.BaseBroker._exception_handler
+    @API._exception_handler
     def fetch_chain_info(self, symbol: str):
         return {
             "id": "n/a", 
@@ -164,7 +128,7 @@ def fetch_chain_info(self, symbol: str):
             "multiplier": 100
         }    
 
-    @base.BaseBroker._exception_handler
+    @API._exception_handler
     def fetch_chain_data(self, symbol: str):
 
         if bool(self.option_cache) and symbol in self.option_cache:
@@ -190,9 +154,8 @@ def fetch_chain_data(self, symbol: str):
         self.option_cache[symbol] = df
         return df
     
-    @base.BaseBroker._exception_handler
+    @API._exception_handler
     def fetch_option_market_data(self, symbol: str):
-      
         _, date, _, _ = self.occ_to_data(symbol)
         chain = self.watch_ticker[symbol].option_chain(date)
         df = chain.loc[symbol]
@@ -202,8 +165,44 @@ def fetch_option_market_data(self, symbol: str):
                 'bid': df['bid'][0]
             }
 
-    # Order functions are not wrapped in the exception handler to prevent duplicate 
-    # orders from being made. 
+    # ------------- Broker methods ------------- #
+    
+    @API._exception_handler
+    def fetch_stock_positions(self):
+        raise Exception("Not implemented")
+
+    @API._exception_handler
+    def fetch_option_positions(self):
+        raise Exception("Not implemented")
+    
+    @API._exception_handler
+    def fetch_crypto_positions(self, key=None):
+        raise Exception("Not implemented")
+    
+    @API._exception_handler
+    def update_option_positions(self, positions: List[Any]):
+        raise Exception("Not implemented")
+
+    @API._exception_handler
+    def fetch_account(self):
+        raise Exception("Not implemented")
+
+    @API._exception_handler
+    def fetch_stock_order_status(self, id):
+        raise Exception("Not implemented")
+    
+    @API._exception_handler
+    def fetch_option_order_status(self, id):
+        raise Exception("Not implemented")
+    
+    @API._exception_handler
+    def fetch_crypto_order_status(self, id):
+        raise Exception("Not implemented")
+    
+    @API._exception_handler
+    def fetch_order_queue(self):
+        raise Exception("Not implemented")
+
     def order_limit(self, 
         side: str, 
         symbol: str,
@@ -216,6 +215,8 @@ def order_limit(self,
     def order_option_limit(self, side: str, symbol: str, quantity: int, limit_price: float, option_type, exp_date: dt.datetime, strike, in_force: str='gtc'):
         raise Exception("Not implemented")
     
+    # ------------- Helper methods ------------- #
+
     def _format_df(self, df: pd.DataFrame, symbol: str):
         df.reset_index(inplace=True)
         ts_name = df.columns[0]
diff --git a/harvest/trader/trader.py b/harvest/trader/trader.py
index c4f57f90..ab57f595 100644
--- a/harvest/trader/trader.py
+++ b/harvest/trader/trader.py
@@ -1,22 +1,21 @@
 # Builtins
 import asyncio
+from harvest.api.paper import PaperBroker
 import re
-import datetime as dt
 import threading
 from logging import warning, debug
-import time
 import sys
-from signal import signal, SIGINT
+from signal import signal, SIGINT, SIGABRT
 from sys import exit
 
 # External libraries
 import pandas as pd
-import pytz
 
 # Submodule imports
 from harvest.utils import *
 from harvest.storage import BaseStorage
-from harvest.broker.dummy import DummyBroker
+from harvest.api.dummy import DummyStreamer
+from harvest.api.paper import PaperBroker
 from harvest.algo import BaseAlgo
 from harvest.storage import BaseStorage
 
@@ -34,18 +33,24 @@ class Trader:
     def __init__(self, streamer=None, broker=None, storage=None):      
         """Initializes the Trader. 
         """
+        signal(SIGINT, self.exit)
 
         if sys.version_info[0] < 3 or sys.version_info[1] < 8:
             raise Exception("Harvest requires Python 3.8 or above.")
 
+        is_dummy = False
         if streamer == None:
             warning("Streamer not specified, using DummyBroker")
-            self.streamer = DummyBroker()
+            self.streamer = DummyStreamer()
+            is_dummy = True
         else:
             self.streamer = streamer
   
         if broker == None:
-            self.broker = self.streamer
+            if is_dummy:
+                self.broker = PaperBroker()
+            else:
+                self.broker = self.streamer
         else:
             self.broker = broker
 
@@ -167,16 +172,13 @@ def _setup_stats(self):
     def start(self, interval='5MIN', aggregations=[], sync = True, kill_switch: bool=False): 
         """Entry point to start the system. 
         
-        :load_watch: If True, all positions will be loaded from the brokerage account. 
-            They will then be added to the watchlist automatically. Set it to False if you
-            do not want to track all positions you currently own. 
-        :interval: Specifies the interval of running the algo. 
-        :aggregations: A list of intervals. The Trader will aggregate data to the intervals specified in this list.
+        :param str? interval: The interval to run the algorithm. defaults to '5MIN'
+        :param list[str]? aggregations: A list of intervals. The Trader will aggregate data to the intervals specified in this list.
             For example, if this is set to ['5MIN', '30MIN'], and interval is '1MIN', the algorithm will have access to 
-            5MIN, 30MIN aggregated candles in addition to 1MIN candles. 
-        :kill_switch: If true, kills the infinite loop in streamer.start so we can test this flow.
+            5MIN, 30MIN aggregated data in addition to 1MIN data. defaults to None
+        :param bool? sync: If true, the system will sync with the broker and fetch current positions and pending orders. defaults to true. 
+        :kill_switch: If true, kills the infinite loop in streamer. Primarily used for testing. defaults to False.
 
-        TODO: If there is no internet connection, the program will shut down before starting. 
         """
         print(f"Starting Harvest...")
 
@@ -212,7 +214,10 @@ async def timeout(self):
         except asyncio.CancelledError:
             debug("Timeout cancelled")
 
-    async def main(self, df_dict, timestamp, flush: bool=False):
+    def main(self, df):
+        self.loop.run_until_complete(self.main_async(df, now()))
+
+    async def main_async(self, df_dict, timestamp, flush: bool=False):
         """ Function called by the broker every minute
         as new stock price data is streamed in. 
 
@@ -470,25 +475,35 @@ def sell_option(self, symbol: str, quantity: int, in_force: str):
         return ret
     
     def set_algo(self, algo):
+        """Specifies the algorithm to use.
+
+        :param Algo algo: The algorithm to use. You can either pass in a single Algo class, or a 
+            list of Algo classes. 
+        """
         if isinstance(algo, list):
             self.algo = algo
         else:
             self.algo = [algo]
     
     def set_symbol(self, symbol):
+        """Specifies the symbol(s) to watch.
+        
+        Cryptocurrencies should be prepended with an `@` to differentiate them from stocks. 
+        For example, '@ETH' will refer to Etherium, while 'ETH' will refer to Ethan Allen Interiors. 
+        If this method was previously called, the symbols specified earlier will be replaced with the
+        new symbols.
+        
+        :symbol str symbol: Ticker Symbol(s) of stock or cryptocurrency to watch. 
+            It can either be a string, or a list of strings. 
+        """
         if isinstance(symbol, list):
             self.watch = symbol
         else:
             self.watch = [symbol]
-
-    def add_symbol(self, symbol):
-        self.watch.append(symbol)
-    
-    def remove_symbol(self, symbol):
-        self.watch.remove(symbol)
     
     def exit(self, signum, frame):
         # TODO: Gracefully exit
         print("\nStopping Harvest...")
         exit(0)
+    
     
\ No newline at end of file
diff --git a/parser.py b/parser.py
index f40513a7..7e54c6ac 100644
--- a/parser.py
+++ b/parser.py
@@ -1,15 +1,12 @@
 from harvest.algo import BaseAlgo as a
+from harvest.trader import Trader as t
+from harvest.trader import BackTester as b
 from docstring_parser import parse
 import json
 
-# Import every function in BaseAlgo
-# I feel like there should be a better way to do this...
-# but for now it will do.
-
-functions = [
-   # a.add_symbol,
-   # a.remove_symbol,
+PATH="./website/pages/docs-content/"
 
+a_func = [
     a.buy,
     a.buy_option,
     a.sell,
@@ -38,41 +35,53 @@
     a.get_quantity,
     a.get_returns,
     a.get_time,
-    
 ]
 
-data = []
+t_func = [
+    t.start,
+    t.set_symbol,
+    t.set_algo,
+]
+
+b_func = [
+    b.start,
+]
+
+def generate_json(class_name, functions):
+    data = []
+    for func in functions:
+
+        doc = parse(func.__doc__)
 
-for func in functions:
-    # Get the docstring as an object
-    doc = parse(func.__doc__)
+        arg_names = ['self']
+        arg_names.extend([p.arg_name for p in doc.params])
+        data.append({
+            "function": func.__name__ + '(' + ', '.join(arg_names) + ')',
+            "index": func.__name__,
+            "short_description": doc.short_description,
+            "long_description": doc.long_description,
+            "params":[
+                {
+                    "name": par.arg_name,
+                    "type": par.type_name,
+                    "desc": par.description[0:par.description.find("defaults")],
+                    "default": par.default,
+                    "optional": par.is_optional
+                } for par in doc.params
+            ],
+            "returns": doc.returns.description if not doc.returns == None else "",
+            "raises": [
+                {
+                    "type": par.type_name,
+                    "desc": par.description,
+                } for par in doc.raises
+            ],
+        })
 
-    arg_names = ['self']
-    arg_names.extend([p.arg_name for p in doc.params])
-    data.append({
-        "function": func.__name__ + '(' + ', '.join(arg_names) + ')',
-        "index": func.__name__,
-        "short_description": doc.short_description,
-        "long_description": doc.long_description,
-        "params":[
-            {
-                "name": par.arg_name,
-                "type": par.type_name,
-                "desc": par.description[0:par.description.find("defaults")],
-                "default": par.default,
-                "optional": par.is_optional
-            } for par in doc.params
-        ],
-        "returns": doc.returns.description if not doc.returns == None else "",
-        "raises": [
-            {
-                "type": par.type_name,
-                "desc": par.description,
-            } for par in doc.raises
-        ],
-    })
+    with open(f'./website/pages/docs-content/{class_name}.json', 'w') as f:
+        json.dump(data, f)
 
-with open('./website/pages/docs-content/data.json', 'w') as f:
-    json.dump(data, f)
-    print(f'Wrote {len(data)} functions to ./website/pages/docs-content/data.json')
+generate_json(a.__name__, a_func)
+generate_json(t.__name__, t_func)
+generate_json(b.__name__, b_func)
 
diff --git a/setup.py b/setup.py
index 3eafc54d..1f98ef68 100644
--- a/setup.py
+++ b/setup.py
@@ -18,7 +18,7 @@ def run(self):
 
 setup(
     name='harvest',
-    packages=['harvest', 'harvest.trader', 'harvest.broker', 'harvest.storage'],
+    packages=['harvest', 'harvest.trader', 'harvest.api', 'harvest.storage'],
     version='0.1.0',
     description='A framework providing a high-level interface for algorithmic trading.',
     author='Harvest Team',
diff --git a/website/pages/components/func.js b/website/pages/components/func.js
index dd03f9b8..ec1f4957 100644
--- a/website/pages/components/func.js
+++ b/website/pages/components/func.js
@@ -10,38 +10,42 @@ function get_def(word){
     }
 }
 
-function generate_params(dict){
-    let params=[]
-    // Parse through method parameters
-    for (let v of dict["params"]){
+function parse_tooltips(text){
 
-        let desc_text = v["desc"];
-        if (desc_text === undefined) desc_text = "";
-    
-        // Match any {} patterns
-        let regex = /{[^{}]+}/g;
-        let matches = desc_text.match(regex);
-        let desc_split = desc_text.split(regex);
+    if (text === undefined) text = "";
+    // Match any {} patterns
+    let regex = /{[^{}]+}/g;
+    let matches = text.match(regex);
+    let desc_split = text.split(regex);
 
-        let nodes = [];
-        let word = '';
-        let tooltip = null;
-        for (let d of desc_split){
-            nodes.push(d);
-            if (matches !== null && matches.length > 0){
-                word = matches.shift().replace(/[{}]/g, '');
-                tooltip = <span>{get_def(word)}</span>;
-                nodes.push(<span className={doc.lookup}>{tooltip}{word}</span>)
-            }
+    let nodes = [];
+    let word = '';
+    let tooltip = null;
+    for (let d of desc_split){
+        nodes.push(d);
+        if (matches !== null && matches.length > 0){
+            word = matches.shift().replace(/[{}]/g, '');
+            tooltip = <span dangerouslySetInnerHTML={{ __html: get_def(word) }}></span>;
+            nodes.push(<span className={doc.lookup}>{tooltip}{word}</span>)
         }
+    }
 
+    return [nodes, matches];
+}
+
+
+function generate_params(dict){
+    let params=[]
+    // Parse through method parameters
+    for (let v of dict["params"]){
+        let nodes = parse_tooltips(v["desc"]);
         params.push(
             <div>
                 <p className={doc.paramName}>• {v["name"]}</p>
                 <p className={doc.paramType}>&nbsp;({v["type"]}):</p>
-                <p>{nodes}</p>
+                <p>{nodes[0]}</p>
                 <p className={doc.paramDef}>{v["default"]}</p>
-                <p>{matches}</p>
+                <p>{nodes[1]}</p>
             </div>
         );   
     }
diff --git a/website/pages/docs-content/BackTester.json b/website/pages/docs-content/BackTester.json
new file mode 100644
index 00000000..65545712
--- /dev/null
+++ b/website/pages/docs-content/BackTester.json
@@ -0,0 +1,1 @@
+[{"function": "start(self, interval, aggregations, source, path)", "index": "start", "short_description": "Runs backtesting. ", "long_description": "The interface is very similar to the Trader class, with some additional parameters for specifying \nbacktesting configurations.", "params": [{"name": "interval", "type": "str", "desc": "The interval to run the algorithm on. ", "default": "'5MIN'", "optional": true}, {"name": "aggregations", "type": "List[str]", "desc": "The aggregations to run. ", "default": "[]", "optional": true}, {"name": "source", "type": "str", "desc": "The source of backtesting data. \n'FETCH' will pull the latest data using the broker (if specified). \n'CSV' will read data from a locally saved CSV file.   \n'PICKLE' will read data from a locally saved pickle file, generated using the Trader class.\n", "default": "'PICKLE'", "optional": true}, {"name": "path", "type": "str", "desc": "The path to the directory which backtesting data is stored. \nThis parameter must be set accordingly if 'source' is set to 'CSV' or 'PICKLE'. ", "default": "'./data'", "optional": true}], "returns": "", "raises": []}]
\ No newline at end of file
diff --git a/website/pages/docs-content/BaseAlgo.json b/website/pages/docs-content/BaseAlgo.json
new file mode 100644
index 00000000..918e4599
--- /dev/null
+++ b/website/pages/docs-content/BaseAlgo.json
@@ -0,0 +1,1 @@
+[{"function": "buy(self, symbol, quantity, in_force, extended)", "index": "buy", "short_description": "Buys the specified asset.", "long_description": "When called, Harvests places a limit order with a limit\nprice 5% higher than the current price.", "params": [{"name": "symbol", "type": "str", "desc": "Symbol of the asset to buy. ", "default": "first symbol in watchlist", "optional": true}, {"name": "quantity", "type": "float", "desc": "Quantity of asset to buy. ", "default": "buys as many as possible", "optional": true}, {"name": "in_force", "type": "str", "desc": "Duration the order is in force. '{gtc}' or '{gtd}'. ", "default": "'gtc'", "optional": true}, {"name": "extended", "type": "str", "desc": "Whether to trade in extended hours or not. ", "default": "False", "optional": true}], "returns": "The following Python dictionary\n- type: str, 'STOCK' or 'CRYPTO'\n- id: str, ID of order\n- symbol: str, symbol of asset", "raises": [{"type": "Exception", "desc": "There is an error in the order process."}]}, {"function": "buy_option(self, symbol, quantity, in_force)", "index": "buy_option", "short_description": "Buys the specified option.", "long_description": null, "params": [{"name": "symbol", "type": "str", "desc": "Symbol of the asset to buy, in {OCC} format", "default": null, "optional": false}, {"name": "quantity", "type": "float", "desc": "Quantity of asset to buy. ", "default": "buys as many as possible", "optional": true}, {"name": "in_force", "type": "str", "desc": "Duration the order is in force. '{gtc}' or '{gtd}'. ", "default": "'gtc'", "optional": true}], "returns": "A dictionary with the following keys:\n- type: 'OPTION'\n- id: ID of order\n- symbol: symbol of asset", "raises": [{"type": "Exception", "desc": "There is an error in the order process."}]}, {"function": "sell(self, symbol, quantity, in_force, extended)", "index": "sell", "short_description": "Sells the specified asset.", "long_description": null, "params": [{"name": "symbol", "type": "str", "desc": "Symbol of the asset to sell. ", "default": "first symbol in watchlist", "optional": true}, {"name": "quantity", "type": "float", "desc": "Quantity of asset to sell ", "default": "sells all", "optional": true}, {"name": "in_force", "type": "str", "desc": "Duration the order is in force. '{gtc}' or '{gtd}'. ", "default": "'gtc'", "optional": true}, {"name": "extended", "type": "str", "desc": "Whether to trade in extended hours or not. ", "default": "False", "optional": true}], "returns": "A dictionary with the following keys:\n- type: str, 'STOCK' or 'CRYPTO'\n- id: str, ID of order\n- symbol: str, symbol of asset", "raises": [{"type": "Exception", "desc": "There is an error in the order process."}]}, {"function": "sell_option(self, symbol, quantity, in_force)", "index": "sell_option", "short_description": "Sells the specified option.", "long_description": null, "params": [{"name": "symbol", "type": "str", "desc": "Symbol of the asset to sell, in {OCC} format", "default": null, "optional": true}, {"name": "quantity", "type": "float", "desc": "Quantity of asset to sell. ", "default": "sells all", "optional": true}, {"name": "in_force", "type": "str", "desc": "Duration the order is in force. '{gtc}' or '{gtd}'. ", "default": "'gtc'", "optional": true}], "returns": "A dictionary with the following keys:\n- type: 'OPTION'\n- id: ID of order\n- symbol: symbol of asset", "raises": [{"type": "Exception", "desc": "There is an error in the order process."}]}, {"function": "await_buy(self, symbol, quantity, in_force, extended)", "index": "await_buy", "short_description": "Buys the specified asset, and hangs the code until the order is filled. ", "long_description": null, "params": [{"name": "symbol", "type": "str", "desc": "Symbol of the asset to buy. ", "default": "first symbol in watchlist", "optional": true}, {"name": "quantity", "type": "float", "desc": "Quantity of asset to buy. ", "default": "buys as many as possible", "optional": true}, {"name": "in_force", "type": "str", "desc": "Duration the order is in force. '{gtc}' or '{gtd}'. ", "default": "'gtc'", "optional": true}, {"name": "extended", "type": "str", "desc": "Whether to trade in extended hours or not. ", "default": "False", "optional": true}], "returns": "A dictionary with the following keys:\n- type: 'STOCK' or 'CRYPTO'\n- id: ID of order\n- symbol: symbol of asset", "raises": [{"type": "Exception", "desc": "There is an error in the order process."}]}, {"function": "await_sell(self, symbol, quantity, in_force, extended)", "index": "await_sell", "short_description": "Sells the specified asset, and hangs the code until the order is filled. ", "long_description": null, "params": [{"name": "symbol", "type": "str", "desc": "Symbol of the asset to sell. ", "default": "first symbol in watchlist", "optional": true}, {"name": "quantity", "type": "float", "desc": "Quantity of asset to sell ", "default": "sells all", "optional": true}, {"name": "in_force", "type": "str", "desc": "Duration the order is in force. '{gtc}' or '{gtd}'. ", "default": "'gtc'", "optional": true}, {"name": "extended", "type": "str", "desc": "Whether to trade in extended hours or not. ", "default": "False", "optional": true}], "returns": "A dictionary with the following keys:\n- type: 'STOCK' or 'CRYPTO'\n- id: ID of order\n- symbol: symbol of asset", "raises": [{"type": "Exception", "desc": "There is an error in the order process."}]}, {"function": "get_option_market_data(self, symbol)", "index": "get_option_market_data", "short_description": "Retrieves data of specified option. ", "long_description": null, "params": [{"name": "symbol", "type": "str", "desc": "{OCC} symbol of optio", "default": null, "optional": true}], "returns": "A dictionary:\n- price: price of option \n- ask: ask price\n- bid: bid price", "raises": []}, {"function": "get_chain_data(self, symbol)", "index": "get_chain_data", "short_description": "Returns the option chain for the specified symbol. ", "long_description": null, "params": [{"name": "symbol", "type": "str", "desc": "symbol of stoc", "default": null, "optional": true}], "returns": "A dataframe with the follwing columns:\n    - exp_date(datetime.datetime): The expiration date\n    - strike(float): Strike price\n    - type(str): 'call' or 'put'\n    - id(str): The unique ID of the option  \n\nThe index is the {OCC} symbol of the option.", "raises": []}, {"function": "get_chain_info(self, symbol)", "index": "get_chain_info", "short_description": "Returns metadata about a stock's option chain", "long_description": null, "params": [{"name": "symbol", "type": "str", "desc": "symbol of stock. ", "default": "first symbol in watchlist", "optional": true}], "returns": "A dict with the following keys:\n- id: ID of the option chain \n- exp_dates: List of expiration dates, in the fomrat \"YYYY-MM-DD\" \n- multiplier: Multiplier of the option, usually 100", "raises": []}, {"function": "ema(self, symbol, period, interval, ref, prices)", "index": "ema", "short_description": "Calculate EMA", "long_description": null, "params": [{"name": "symbol", "type": "str", "desc": "Symbol to perform calculation on. ", "default": "first symbol in watchlist", "optional": true}, {"name": "period", "type": "int", "desc": "Period of EMA. ", "default": "14", "optional": true}, {"name": "interval", "type": "str", "desc": "Interval to perform the calculation. ", "default": "interval of algorithm", "optional": true}, {"name": "ref", "type": "str", "desc": "'close', 'open', 'high', or 'low'. ", "default": "'close'", "optional": true}, {"name": "prices", "type": "list", "desc": "When specified, this function will use the values provided in the \nlist to perform calculations and ignore other parameters. ", "default": "None", "optional": true}], "returns": "A list in numpy format, containing EMA values", "raises": []}, {"function": "rsi(self, symbol, period, interval, ref, prices)", "index": "rsi", "short_description": "Calculate RSI", "long_description": null, "params": [{"name": "symbol", "type": "str", "desc": "Symbol to perform calculation on. ", "default": "first symbol in watchlist", "optional": true}, {"name": "period", "type": "int", "desc": "Period of RSI. ", "default": "14", "optional": true}, {"name": "interval", "type": "str", "desc": "Interval to perform the calculation. ", "default": "interval of algorithm", "optional": true}, {"name": "ref", "type": "str", "desc": "'close', 'open', 'high', or 'low'. ", "default": "'close'", "optional": true}, {"name": "prices", "type": "list", "desc": "When specified, this function will use the values provided in the \nlist to perform calculations and ignore other parameters. ", "default": "None", "optional": true}], "returns": "A list in numpy format, containing RSI values", "raises": []}, {"function": "sma(self, symbol, period, interval, ref, prices)", "index": "sma", "short_description": "Calculate SMA", "long_description": null, "params": [{"name": "symbol", "type": "str", "desc": "Symbol to perform calculation on. ", "default": "first symbol in watchlist", "optional": true}, {"name": "period", "type": "int", "desc": "Period of SMA. ", "default": "14", "optional": true}, {"name": "interval", "type": "str", "desc": "Interval to perform the calculation. ", "default": "interval of algorithm", "optional": true}, {"name": "ref", "type": "str", "desc": "'close', 'open', 'high', or 'low'. ", "default": "'close'", "optional": true}, {"name": "prices", "type": "list", "desc": "When specified, this function will use the values provided in the \nlist to perform calculations and ignore other parameters. ", "default": "None", "optional": true}], "returns": "A list in numpy format, containing SMA values", "raises": []}, {"function": "bbands(self, symbol, period, interval, ref, dev, prices)", "index": "bbands", "short_description": "Calculate Bollinger Bands", "long_description": null, "params": [{"name": "symbol", "type": "str", "desc": "Symbol to perform calculation on. ", "default": "first symbol in watchlist", "optional": true}, {"name": "period", "type": "int", "desc": "Period of BBands. ", "default": "14", "optional": true}, {"name": "interval", "type": "str", "desc": "Interval to perform the calculation. ", "default": "interval of algorithm", "optional": true}, {"name": "ref", "type": "str", "desc": "'close', 'open', 'high', or 'low'. ", "default": "'close'", "optional": true}, {"name": "dev", "type": "float", "desc": "Standard deviation of the bands. ", "default": "1.0", "optional": true}, {"name": "prices", "type": "list", "desc": "When specified, this function will use the values provided in the \nlist to perform calculations and ignore other parameters. ", "default": "None", "optional": true}], "returns": "A tuple of numpy lists, each a list of BBand top, average, and bottom values", "raises": []}, {"function": "get_account_buying_power(self)", "index": "get_account_buying_power", "short_description": "Returns the current buying power of the user", "long_description": null, "params": [], "returns": "The current buying power as a float.", "raises": []}, {"function": "get_account_equity(self)", "index": "get_account_equity", "short_description": "Returns the current equity.", "long_description": null, "params": [], "returns": "The current equity as a float.", "raises": []}, {"function": "get_candle(self, symbol)", "index": "get_candle", "short_description": "Returns the most recent candle as a pandas DataFrame", "long_description": "This function is not compatible with options.", "params": [{"name": "symbol", "type": "str", "desc": "Symbol of stock or crypto asset. ", "default": "first symbol in watchlist", "optional": true}], "returns": "Price of asset as a dataframe with the following columns:\n    - open\n    - high\n    - low\n    - close\n    - volume\n\nThe index is a datetime object", "raises": [{"type": "Exception", "desc": "If symbol is not in the watchlist."}]}, {"function": "get_candle_list(self, symbol)", "index": "get_candle_list", "short_description": "Returns the candles of an asset as a pandas DataFrame", "long_description": "This function is not compatible with options.", "params": [{"name": "symbol", "type": "str", "desc": "Symbol of stock or crypto asset. ", "default": "first symbol in watchlist", "optional": true}], "returns": "Prices of asset as a dataframe with the following columns:\n    - open\n    - high\n    - low\n    - close\n    - volume\n\nThe index is a datetime object", "raises": [{"type": "Exception", "desc": "If symbol is not in the watchlist."}]}, {"function": "get_cost(self, symbol)", "index": "get_cost", "short_description": "Returns the average cost of a specified asset. ", "long_description": null, "params": [{"name": "symbol", "type": "str", "desc": "Symbol of asset. ", "default": "first symbol in watchlist", "optional": true}], "returns": "Average cost of asset. Returns None if asset is not being tracked.", "raises": [{"type": "Exception", "desc": "If symbol is not currently owned."}]}, {"function": "get_date(self)", "index": "get_date", "short_description": "Returns the current date.", "long_description": null, "params": [], "returns": "The current date as a datetime object", "raises": []}, {"function": "get_datetime(self)", "index": "get_datetime", "short_description": "Returns the current date and time.", "long_description": "This returns the current time, which is different from the timestamp\non a ticker. For example, if you are running an algorithm every 5 minutes,\nat 11:30am you will get a ticker for 11:25am. This function will return \n11:30am.", "params": [], "returns": "The current date and time as a datetime object", "raises": []}, {"function": "get_price(self, symbol)", "index": "get_price", "short_description": "Returns the current price of a specified asset. ", "long_description": null, "params": [{"name": "symbol", "type": "str", "desc": "Symbol of asset. ", "default": "first symbol in watchlist", "optional": true}], "returns": "Price of asset.", "raises": [{"type": "Exception", "desc": "If symbol is not in the watchlist."}]}, {"function": "get_price_list(self, symbol, interval, ref)", "index": "get_price_list", "short_description": "Returns a list of recent prices for an asset. ", "long_description": "This function is not compatible with options.", "params": [{"name": "symbol", "type": "str", "desc": "Symbol of stock or crypto asset. ", "default": "first symbol in watchlist", "optional": true}, {"name": "interval", "type": "str", "desc": "Interval of data. ", "default": "the interval of the algorithm", "optional": true}, {"name": "ref", "type": "str", "desc": "'close', 'open', 'high', or 'low'. ", "default": "'close'", "optional": true}], "returns": "List of prices", "raises": []}, {"function": "get_quantity(self, symbol)", "index": "get_quantity", "short_description": "Returns the quantity owned of a specified asset. ", "long_description": null, "params": [{"name": "symbol", "type": "str", "desc": "Symbol of asset. ", "default": "first symbol in watchlist", "optional": true}], "returns": "Quantity of asset.", "raises": [{"type": "Exception", "desc": "If symbol is not currently owned."}]}, {"function": "get_returns(self, symbol)", "index": "get_returns", "short_description": "Returns the return of a specified asset.", "long_description": null, "params": [{"name": "symbol", "type": "str", "desc": "Symbol of stock, crypto, or option. Options should be in {OCC} format. \n", "default": "first symbol in watchlist", "optional": true}], "returns": "Return of asset, expressed as a decimal.", "raises": []}, {"function": "get_time(self)", "index": "get_time", "short_description": "Returns the current hour and minute.", "long_description": "This returns the current time, which is different from the timestamp\non a ticker. For example, if you are running an algorithm every 5 minutes,\nat 11:30am you will get a ticker for 11:25am. This function will return \n11:30am.", "params": [], "returns": "The current time as a datetime object", "raises": []}]
\ No newline at end of file
diff --git a/website/pages/docs-content/Trader.json b/website/pages/docs-content/Trader.json
new file mode 100644
index 00000000..6c29e10f
--- /dev/null
+++ b/website/pages/docs-content/Trader.json
@@ -0,0 +1,1 @@
+[{"function": "start(self, interval, aggregations, sync)", "index": "start", "short_description": "Entry point to start the system. ", "long_description": null, "params": [{"name": "interval", "type": "str", "desc": "The interval to run the algorithm. ", "default": "'5MIN'", "optional": true}, {"name": "aggregations", "type": "list[str]", "desc": "A list of intervals. The Trader will aggregate data to the intervals specified in this list.\nFor example, if this is set to ['5MIN', '30MIN'], and interval is '1MIN', the algorithm will have access to \n5MIN, 30MIN aggregated data in addition to 1MIN data. ", "default": "None", "optional": true}, {"name": "sync", "type": "bool", "desc": "If true, the system will sync with the broker and fetch current positions and pending orders. ", "default": "true", "optional": true}], "returns": "", "raises": []}, {"function": "set_symbol(self)", "index": "set_symbol", "short_description": "Specifies the symbol(s) to watch.", "long_description": "Cryptocurrencies should be prepended with an `@` to differentiate them from stocks. \nFor example, '@ETH' will refer to Etherium, while 'ETH' will refer to Ethan Allen Interiors. \nIf this method was previously called, the symbols specified earlier will be replaced with the\nnew symbols.", "params": [], "returns": "", "raises": []}, {"function": "set_algo(self, algo)", "index": "set_algo", "short_description": "Specifies the algorithm to use.", "long_description": null, "params": [{"name": "algo", "type": "Algo", "desc": "The algorithm to use. You can either pass in a single Algo class, or a \nlist of Algo classes", "default": null, "optional": false}], "returns": "", "raises": []}]
\ No newline at end of file
diff --git a/website/pages/docs-content/algo.json b/website/pages/docs-content/algo.json
deleted file mode 100644
index 27cf44c5..00000000
--- a/website/pages/docs-content/algo.json
+++ /dev/null
@@ -1,182 +0,0 @@
-[
-    {
-        "name": "buy(self, symbol, quantity, in_force, extended)",
-        "short": "buy",
-        "description": "Buys the specified asset",
-        "params": [
-            {
-                "name": "symbol",
-                "type":"str",
-                "description":"Ticker symbol of the asset to buy.",
-                "default":"First symbol in 'watch'"
-            },{
-                "name": "quantity",
-                "type":"float",
-                "description":"Quantity of asset to buy.",
-                "default":"1"
-            },{
-                "name": "in_force",
-                "type":"str",
-                "description":"Duration the order is in force. <span>'gtd'</span> or 'gtc'",
-                "default":"gtd"
-            },{
-                "name": "extended",
-                "type":"str",
-                "description":"Whether to trade in extended hours or not",
-                "default":"False"
-            }],
-        "returns": "A dictionary with the following keys:\n\t- type: 'STOCK' or 'CRYPTO'\n\t- id: ID of order\n\t- symbol: symbol of asset",
-        "raises": "Raises an exception if order fails."
-    },
-    {
-        "name": "sell(self, symbol, quantity, in_force, extended)",
-        "short": "sell",
-        "description": "Sells the specified asset",
-        "params": [
-            {
-                "name": "symbol",
-                "type":"str",
-                "description":"Symbol of the asset to sell",
-                "default":"First symbol in 'watch'"
-            },{
-                "name": "quantity",
-                "type":"float",
-                "description":"Quantity of asset to sell"
-            },{
-                "name": "in_force",
-                "type":"str",
-                "description":"Duration the order is in force"
-            },{
-                "name": "extended",
-                "type":"str",
-                "description":"Whether to trade in extended hours or not"
-            }],
-        "returns": "A dictionary with the following keys:\n\t- type: 'STOCK' or 'CRYPTO'\n\t- id: ID of order\n\t- symbol: symbol of asset",
-        "raises": "Raises an exception if order fails."
-    },
-    {
-        "name": "await_buy(self, symbol, quantity, in_force, extended)",
-        "short": "await_buy",
-        "description": "Buys the specified asset, and hangs the code until the order is filled. ",
-        "params": [
-            {
-                "name": "symbol",
-                "type":"str",
-                "description":"Symbol of the asset to sell",
-                "default":"First symbol in 'watch'"
-            },{
-                "name": "quantity",
-                "type":"float",
-                "description":"Quantity of asset to sell"
-            },{
-                "name": "in_force",
-                "type":"str",
-                "description":"Duration the order is in force"
-            },{
-                "name": "extended",
-                "type":"str",
-                "description":"Whether to trade in extended hours or not"
-            }],
-        "returns": "A dictionary with the following keys:\n\t- type: 'STOCK' or 'CRYPTO'\n\t- id: ID of order\n\t- symbol: symbol of asset",
-        "raises": "Raises an exception if order fails."
-    },{
-        "name": "await_sell(self, symbol, quantity, in_force, extended)",
-        "short": "await_sell",
-        "description": "Sells the specified asset, and hangs the code until the order is filled. ",
-        "params": [
-            {
-                "name": "symbol",
-                "type":"str",
-                "description":"Symbol of the asset to sell",
-                "default":"First symbol in 'watch'"
-            },{
-                "name": "quantity",
-                "type":"float",
-                "description":"Quantity of asset to sell"
-            },{
-                "name": "in_force",
-                "type":"str",
-                "description":"Duration the order is in force"
-            },{
-                "name": "extended",
-                "type":"str",
-                "description":"Whether to trade in extended hours or not"
-            }],
-        "returns": "A dictionary with the following keys:\n\t- type: 'STOCK' or 'CRYPTO'\n\t- id: ID of order\n\t- symbol: symbol of asset",
-        "raises": "Raises an exception if order fails."
-    },{
-        "name": "buy_option(self, symbol, quantity, in_force)",
-        "short": "buy_option",
-        "description": "Buys the specified option",
-        "params": [
-            {
-                "name": "symbol",
-                "type":"str",
-                "description":"Ticker symbol of the asset to buy.",
-                "default":"First symbol in 'watch'"
-            },{
-                "name": "quantity",
-                "type":"float",
-                "description":"Quantity of asset to buy.",
-                "default":"1"
-            },{
-                "name": "in_force",
-                "type":"str",
-                "description":"Duration the order is in force. <span>'gtd'</span> or 'gtc'",
-                "default":"gtd"
-            }],
-        "returns": "A dictionary with the following keys:\n\t- type: 'OPTION'\n\t- id: ID of order\n\t- symbol: symbol of asset",
-        "raises": "Raises an exception if order fails."
-    },
-    {
-        "name": "sell_option(self, symbol, quantity, in_force)",
-        "short": "sell_option",
-        "description": "Sells the specified option",
-        "params": [
-            {
-                "name": "symbol",
-                "type":"str",
-                "description":"Symbol of the asset to sell",
-                "default":"First symbol in 'watch'"
-            },{
-                "name": "quantity",
-                "type":"float",
-                "description":"Quantity of asset to sell"
-            },{
-                "name": "in_force",
-                "type":"str",
-                "description":"Duration the order is in force"
-            }],
-        "returns": "A dictionary with the following keys:\n\t- type: 'OPTION'\n\t- id: ID of order\n\t- symbol: symbol of asset",
-        "raises": "Raises an exception if order fails."
-    },
-    {
-        "name": "get_chain_info(self, symbol)",
-        "short": "get_chain_info",
-        "description": "Returns the option chain for a given stock",
-        "params": [
-            {
-                "name": "symbol",
-                "type":"str",
-                "description":"Symbol of stock",
-                "default":"Required"
-            }],
-        "returns": "A dict with the following keys:- id: ID of the option chain - exp_dates: List of expiration dates, in the format 'YYYY-MM-DD' - multiplier: Multiplier of the option, usually 100 ",
-        "raises": "Raises an exception if it fails."
-    },
-    {
-        "name": "get_option_market_data(self, symbol)",
-        "short": "get_option_market_data",
-        "description": "Retrieves data of specified option.",
-        "params": [
-            {
-                "name": "symbol",
-                "type":"str",
-                "description":"OCC Symbol of stock",
-                "default":"Required"
-            }],
-        "returns": "A dictionary:- price: price of option - ask: ask price- bid: bid price",
-        "raises": "Raises an exception if it fails."
-    }
-]
-
diff --git a/website/pages/docs-content/data.json b/website/pages/docs-content/data.json
deleted file mode 100644
index 5da48a9e..00000000
--- a/website/pages/docs-content/data.json
+++ /dev/null
@@ -1,1 +0,0 @@
-[{"function": "buy(self, symbol, quantity, in_force, extended)", "index": "buy", "short_description": "Buys the specified asset.", "long_description": "When called, Harvests places a limit order with a limit\nprice 5% higher than the current price.", "params": [{"name": "symbol", "type": "str", "desc": "Symbol of the asset to buy. ", "default": "first symbol in watch", "optional": true}, {"name": "quantity", "type": "float", "desc": "Quantity of asset to buy. ", "default": "1", "optional": true}, {"name": "in_force", "type": "str", "desc": "Duration the order is in force. '{gtc}' or '{gtd}'. ", "default": "'gtc'", "optional": true}, {"name": "extended", "type": "str", "desc": "Whether to trade in extended hours or not. ", "default": "False", "optional": true}], "returns": "The following Python dictionary\n- type: 'STOCK' or 'CRYPTO'\n- id: ID of order\n- symbol: symbol of asset", "raises": [{"type": "Exception", "desc": "There is an error in the order process."}]}, {"function": "buy_option(self, symbol, quantity, in_force)", "index": "buy_option", "short_description": "Buys the specified option.", "long_description": null, "params": [{"name": "symbol", "type": null, "desc": "Symbol of the asset to buy, in OCC format", "default": null, "optional": null}, {"name": "quantity", "type": null, "desc": "Quantity of asset to bu", "default": null, "optional": null}, {"name": "in_force", "type": null, "desc": "Duration the order is in forc", "default": null, "optional": null}], "returns": "A dictionary with the following keys:\n- type: 'OPTION'\n- id: ID of order\n- symbol: symbol of asset\nRaises an exception if order fails.", "raises": []}, {"function": "sell(self, symbol, quantity, in_force, extended)", "index": "sell", "short_description": "Sells the specified asset.", "long_description": null, "params": [{"name": "symbol", "type": null, "desc": "Symbol of the asset to sel", "default": null, "optional": null}, {"name": "quantity", "type": null, "desc": "Quantity of asset to sel", "default": null, "optional": null}, {"name": "in_force", "type": null, "desc": "Duration the order is in forc", "default": null, "optional": null}, {"name": "extended", "type": null, "desc": "Whether to trade in extended hours or not", "default": null, "optional": null}], "returns": "A dictionary with the following keys:\n    - type: 'STOCK' or 'CRYPTO'\n    - id: ID of order\n    - symbol: symbol of asset\n\nRaises an exception if order fails.", "raises": []}, {"function": "sell_option(self, symbol, quantity, in_force)", "index": "sell_option", "short_description": "Sells the specified option.", "long_description": null, "params": [{"name": "symbol", "type": null, "desc": "Symbol of the asset to buy, in OCC format", "default": null, "optional": null}, {"name": "quantity", "type": null, "desc": "Quantity of asset to bu", "default": null, "optional": null}, {"name": "in_force", "type": null, "desc": "Duration the order is in forc", "default": null, "optional": null}], "returns": "A dictionary with the following keys:\n- type: 'OPTION'\n- id: ID of order\n- symbol: symbol of asset\nRaises an exception if order fails.", "raises": []}, {"function": "await_buy(self, symbol, quantity, in_force, extended)", "index": "await_buy", "short_description": "Buys the specified asset, and hangs the code until the order is filled. ", "long_description": null, "params": [{"name": "symbol", "type": null, "desc": "Symbol of the asset to bu", "default": null, "optional": null}, {"name": "quantity", "type": null, "desc": "Quantity of asset to bu", "default": null, "optional": null}, {"name": "in_force", "type": null, "desc": "Duration the order is in forc", "default": null, "optional": null}, {"name": "extended", "type": null, "desc": "Whether to trade in extended hours or not", "default": null, "optional": null}], "returns": "A dictionary with the following keys:\n- type: 'STOCK' or 'CRYPTO'\n- id: ID of order\n- symbol: symbol of asset\nRaises an exception if order fails.", "raises": []}, {"function": "await_sell(self, symbol, quantity, in_force, extended)", "index": "await_sell", "short_description": "Sells the specified asset, and hangs the code until the order is filled. ", "long_description": null, "params": [{"name": "symbol", "type": null, "desc": "Symbol of the asset to bu", "default": null, "optional": null}, {"name": "quantity", "type": null, "desc": "Quantity of asset to bu", "default": null, "optional": null}, {"name": "in_force", "type": null, "desc": "Duration the order is in forc", "default": null, "optional": null}, {"name": "extended", "type": null, "desc": "Whether to trade in extended hours or not", "default": null, "optional": null}], "returns": "A dictionary with the following keys:\n- type: 'STOCK' or 'CRYPTO'\n- id: ID of order\n- symbol: symbol of asset\nRaises an exception if order fails.", "raises": []}, {"function": "get_option_market_data(self, symbol)", "index": "get_option_market_data", "short_description": "Retrieves data of specified option. ", "long_description": null, "params": [{"name": "symbol", "type": null, "desc": "Occ symbol of optio", "default": null, "optional": null}], "returns": "A dictionary:\n- price: price of option \n- ask: ask price\n- bid: bid price\n}", "raises": []}, {"function": "get_chain_data(self, symbol)", "index": "get_chain_data", "short_description": "Returns the option chain for the specified symbol. ", "long_description": null, "params": [{"name": "symbol", "type": null, "desc": "symbol of stoc", "default": null, "optional": null}], "returns": "A dataframe in the following format:\n            exp_date strike  type    id\n    OCC\n    ---     ---      ---     ---     ---     \n- OCC: the chain symbol in OCC format", "raises": []}, {"function": "get_chain_info(self, symbol)", "index": "get_chain_info", "short_description": "Returns information about the symbol's options", "long_description": null, "params": [{"name": "symbol", "type": null, "desc": "symbol of stoc", "default": null, "optional": null}], "returns": "A dict with the following keys:\n- id: ID of the option chain \n- exp_dates: List of expiration dates, in the fomrat \"YYYY-MM-DD\" \n- multiplier: Multiplier of the option, usually 100", "raises": []}, {"function": "ema(self, symbol, period, interval, ref)", "index": "ema", "short_description": "Calculate EMA", "long_description": null, "params": [{"name": "symbol", "type": null, "desc": "Symbol to perform calculation o", "default": null, "optional": null}, {"name": "period", "type": null, "desc": "Period of EM", "default": null, "optional": null}, {"name": "interval", "type": null, "desc": "Interval to perform the calculatio", "default": null, "optional": null}, {"name": "ref", "type": null, "desc": "'close', 'open', 'high', or 'low", "default": null, "optional": null}], "returns": "A list in numpy format, containing EMA values", "raises": []}, {"function": "rsi(self, symbol, period, interval, ref)", "index": "rsi", "short_description": "Calculate RSI", "long_description": null, "params": [{"name": "symbol", "type": null, "desc": "Symbol to perform calculation o", "default": null, "optional": null}, {"name": "period", "type": null, "desc": "Period of RS", "default": null, "optional": null}, {"name": "interval", "type": null, "desc": "Interval to perform the calculatio", "default": null, "optional": null}, {"name": "ref", "type": null, "desc": "'close', 'open', 'high', or 'low", "default": null, "optional": null}], "returns": "A list in numpy format, containing RSI values", "raises": []}, {"function": "sma(self, symbol, period, interval, ref)", "index": "sma", "short_description": "Calculate SMA", "long_description": null, "params": [{"name": "symbol", "type": null, "desc": "Symbol to perform calculation o", "default": null, "optional": null}, {"name": "period", "type": null, "desc": "Period of SM", "default": null, "optional": null}, {"name": "interval", "type": null, "desc": "Interval to perform the calculatio", "default": null, "optional": null}, {"name": "ref", "type": null, "desc": "'close', 'open', 'high', or 'low", "default": null, "optional": null}], "returns": "A list in numpy format, containing SMA values", "raises": []}, {"function": "bbands(self, symbol, period, interval, ref, dev)", "index": "bbands", "short_description": "Calculate Bollinger Bands", "long_description": null, "params": [{"name": "symbol", "type": null, "desc": "Symbol to perform calculation o", "default": null, "optional": null}, {"name": "period", "type": null, "desc": "Period of BBan", "default": null, "optional": null}, {"name": "interval", "type": null, "desc": "Interval to perform the calculatio", "default": null, "optional": null}, {"name": "ref", "type": null, "desc": "'close', 'open', 'high', or 'low", "default": null, "optional": null}, {"name": "dev", "type": null, "desc": "Standard deviation of the band", "default": null, "optional": null}], "returns": "A tuple of numpy lists, each a list of BBand top, average, and bottom values", "raises": []}, {"function": "bbands_raw(self, arr, symbol, period, interval, ref, dev)", "index": "bbands_raw", "short_description": "Calculate Bollinger Bands using given data", "long_description": null, "params": [{"name": "arr", "type": null, "desc": "List of prices as float to use in calculation of bollinger ban", "default": null, "optional": null}, {"name": "symbol", "type": null, "desc": "Symbol to perform calculation o", "default": null, "optional": null}, {"name": "period", "type": null, "desc": "Period of BBan", "default": null, "optional": null}, {"name": "interval", "type": null, "desc": "Interval to perform the calculatio", "default": null, "optional": null}, {"name": "ref", "type": null, "desc": "'close', 'open', 'high', or 'low", "default": null, "optional": null}, {"name": "dev", "type": null, "desc": "Standard deviation of the band", "default": null, "optional": null}], "returns": "A tuple of numpy lists, each a list of BBand top, average, and bottom values", "raises": []}, {"function": "get_account_buying_power(self)", "index": "get_account_buying_power", "short_description": null, "long_description": null, "params": [], "returns": "", "raises": []}, {"function": "get_account_equity(self)", "index": "get_account_equity", "short_description": null, "long_description": null, "params": [], "returns": "", "raises": []}, {"function": "get_candle(self)", "index": "get_candle", "short_description": null, "long_description": null, "params": [], "returns": "", "raises": []}, {"function": "get_candle_list(self)", "index": "get_candle_list", "short_description": null, "long_description": null, "params": [], "returns": "", "raises": []}, {"function": "get_cost(self, symbol)", "index": "get_cost", "short_description": "Returns the average cost of a specified asset. ", "long_description": null, "params": [{"name": "symbol", "type": null, "desc": "Symbol of asse", "default": null, "optional": null}], "returns": "Average cost of asset. Returns None if asset is not being tracked.", "raises": []}, {"function": "get_date(self)", "index": "get_date", "short_description": null, "long_description": null, "params": [], "returns": "", "raises": []}, {"function": "get_datetime(self)", "index": "get_datetime", "short_description": null, "long_description": null, "params": [], "returns": "", "raises": []}, {"function": "get_price(self)", "index": "get_price", "short_description": null, "long_description": null, "params": [], "returns": "", "raises": []}, {"function": "get_price_list(self, symbol, interval)", "index": "get_price_list", "short_description": "Returns a list of recent prices for an asset. ", "long_description": null, "params": [{"name": "symbol", "type": null, "desc": "Symbol of asset", "default": null, "optional": null}, {"name": "interval", "type": null, "desc": "Interval of data", "default": null, "optional": null}], "returns": "Average cost of asset. Returns None if asset is not being tracked.", "raises": []}, {"function": "get_quantity(self, symbol)", "index": "get_quantity", "short_description": "Returns the quantity owned of a specified asset. ", "long_description": null, "params": [{"name": "symbol", "type": null, "desc": "Symbol of asse", "default": null, "optional": null}], "returns": "Quantity of asset. 0 if asset is not owned.", "raises": []}, {"function": "get_returns(self, symbol)", "index": "get_returns", "short_description": "Returns the return of a specified asset. ", "long_description": null, "params": [{"name": "symbol", "type": null, "desc": "Symbol of stock, crypto, or option. Options should be in OCC format", "default": null, "optional": null}], "returns": "Return of asset, expressed as a decimal. Returns None if asset is not owned.", "raises": []}, {"function": "get_time(self)", "index": "get_time", "short_description": null, "long_description": null, "params": [], "returns": "", "raises": []}, {"function": "get_watch(self)", "index": "get_watch", "short_description": null, "long_description": null, "params": [], "returns": "", "raises": []}]
\ No newline at end of file
diff --git a/website/pages/docs-content/def.json b/website/pages/docs-content/def.json
index 6d01b1f8..082636ec 100644
--- a/website/pages/docs-content/def.json
+++ b/website/pages/docs-content/def.json
@@ -1,3 +1,5 @@
 {
-    "gtc":"Good-Til-Cancelled"
-}
\ No newline at end of file
+    "gtc":"Good-Til-Cancelled. Order will be pending until it is filled or cancelled",
+    "gtd":"Good-Til-Day. If the order is not filled by the end of the trading day, it will be cancelled",
+    "OCC": "A standard for identifying option securities. See <a href='https://en.wikipedia.org/wiki/Option_symbol'>the Wikipedia page</a> for details."
+}
diff --git a/website/pages/docs.js b/website/pages/docs.js
index b78e2018..5e97a2ba 100644
--- a/website/pages/docs.js
+++ b/website/pages/docs.js
@@ -8,17 +8,30 @@ import styles from '../styles/Home.module.scss'
 import doc from '../styles/Doc.module.css'
 import rc from '../styles/rowcrop.module.css'
 
-import algos from './docs-content/data.json'
+import algos from './docs-content/BaseAlgo.json'
+import trader from './docs-content/Trader.json'
+import backtester from './docs-content/BackTester.json'
 
 export default function Home() {
   let algo_items = [];
-  let algo_func = [];
+  let trader_items = [];
+  let backtester_items = [];
+  // let algo_func = [];
   for (let val of algos){
     algo_items.push(<Function dict={val}></Function>);
-    algo_func.push(<Link 
-                    href={'/docs#'+val["index"]}
-                    passHref>
-                      <li>{val["index"]}</li></Link>)
+    // algo_func.push(<Link 
+    //                 href={'/docs#'+val["index"]}
+    //                 passHref>
+    //                   <li>{val["index"]}</li></Link>)
+  }
+  for (let val of algos){
+    algo_items.push(<Function dict={val}></Function>);
+  }
+  for (let val of trader){
+    trader_items.push(<Function dict={val}></Function>);
+  }
+  for (let val of backtester){
+    backtester_items.push(<Function dict={val}></Function>);
   }
 
   return (
@@ -28,14 +41,17 @@ export default function Home() {
       <main className={styles.main}>
           <nav className={`${doc.nav} ${rc.card}`}>
             <h3>Algo</h3>
-            <ul>
-            {algo_func}
-            </ul>
+            <h3>Trader</h3>
+            <h3>BackTester</h3>
           </nav>
         <section className={styles.section}>
           <h1>Documentation</h1>
           <h2 className={doc.className}>harvest.Algo</h2>
           {algo_items}
+          <h2 className={doc.className}>harvest.Trader</h2>
+          {trader_items}
+          <h2 className={doc.className}>harvest.BackTester</h2>
+          {backtester_items}
         </section>
       </main>
 
diff --git a/website/pages/tutorial/starter.js b/website/pages/tutorial/starter.js
index b0b937d2..0ee442d7 100644
--- a/website/pages/tutorial/starter.js
+++ b/website/pages/tutorial/starter.js
@@ -95,7 +95,7 @@ t = Trader( RobinhoodBroker() )`}>
                 </p>
                 <CodeBlock
                     lang="python"
-                    value={`t.add_symbol('TWTR')`}></CodeBlock>
+                    value={`t.set_symbol('TWTR')`}></CodeBlock>
                 
                 <p>At this point, we define our algorithm. Algorithms are created by extending the BaseAlgo class.
                 </p>
@@ -103,10 +103,10 @@ t = Trader( RobinhoodBroker() )`}>
                 <CodeBlock
                     lang="python"
                     value={`class Twitter(BaseAlgo):
-                    def algo_init(self):
+                    def setup(self):
                         pass
 
-                    def handler(self, meta):
+                    def main(self, meta):
                         pass`}></CodeBlock>
 
                 <p>every Algo must define two functions
@@ -117,10 +117,10 @@ t = Trader( RobinhoodBroker() )`}>
                 <CodeBlock
                     lang="python"
                     value={`class Twitter(BaseAlgo):
-    def algo_init(self):
+    def setup(self):
         self.hold = False
 
-    def handler(self, meta):
+    def main(self, meta):
         if self.hold:
             self.sell('TWTR', 1)
             self.hold = False
@@ -137,10 +137,10 @@ from harvest.trader import Trader
 from harvest.broker.robinhood import RobinhoodBroker
 
 class Twitter(BaseAlgo):
-    def algo_init(self):
+    def setup(self):
         self.hold = False
 
-    def handler(self, meta):
+    def main(self, meta):
         if self.hold:
             self.sell('TWTR', 1)    
             self.hold = False
@@ -150,9 +150,9 @@ class Twitter(BaseAlgo):
 
 if __name__ == "__main__":
     t = Trader( RobinhoodBroker(), None )
-    t.add_symbol('TWTR')
+    t.set_symbol('TWTR')
     t.set_algo(Twitter())
-    t.run(interval='1DAY')`}></CodeBlock>
+    t.run('1DAY')`}></CodeBlock>
 
                 <p>By specifying interval=&#39;1DAY&#39; in run, the handler function will be called once every day.</p>
 


Test patch:
diff --git a/harvest/trader/tester.py b/harvest/trader/tester.py
index 43bd3b94..9b38a481 100644
--- a/harvest/trader/tester.py
+++ b/harvest/trader/tester.py
@@ -13,7 +13,8 @@
 # Submodule imports
 from harvest.storage import PickleStorage
 import harvest.trader.trader as trader
-from harvest.broker.dummy import DummyBroker
+from harvest.api.dummy import DummyStreamer
+from harvest.api.paper import PaperBroker
 from harvest.utils import *
 
 class BackTester(trader.Trader):
@@ -26,10 +27,10 @@ def __init__(self, streamer=None, config={}):
         """Initializes the TestTrader. 
         """
         if streamer == None:
-            self.streamer = DummyBroker()
+            self.streamer = DummyStreamer()
         else:
             self.streamer = streamer
-        self.broker = DummyBroker()
+        self.broker = PaperBroker()
 
         self.watch = []             # List of stocks to watch
 
@@ -178,11 +179,21 @@ def setup(self, source, interval, aggregations=None, path=None):
             
         self.load_watch = True
 
-    def start(self, interval: str='5MIN', aggregations: List[Any]=[], source: str='LOCAL', path: str="./data", kill_switch: bool=False):
+    def start(self, interval: str='5MIN', aggregations: List[Any]=[], source: str='PICKLE', path: str="./data", kill_switch: bool=False):
         """Runs backtesting. 
 
-        :source: 'LOCAL' if backtesting data is in a local file, 'FETCH' if the streamer should 
-            be used to download latest data. 
+        The interface is very similar to the Trader class, with some additional parameters for specifying 
+        backtesting configurations.
+
+        :param str? interval: The interval to run the algorithm on. defaults to '5MIN'.
+        :param List[str]? aggregations: The aggregations to run. defaults to [].
+        :param str? source: The source of backtesting data. 
+            'FETCH' will pull the latest data using the broker (if specified). 
+            'CSV' will read data from a locally saved CSV file.   
+            'PICKLE' will read data from a locally saved pickle file, generated using the Trader class.
+            defaults to 'PICKLE'.
+        :param str? path: The path to the directory which backtesting data is stored. 
+            This parameter must be set accordingly if 'source' is set to 'CSV' or 'PICKLE'. defaults to './data'.
         """
 
         self.setup(source, interval, aggregations, path)
diff --git a/test/test_algo.py b/test/test_algo.py
index d546c80b..21bd88e4 100644
--- a/test/test_algo.py
+++ b/test/test_algo.py
@@ -1,4 +1,5 @@
 # Builtins
+from harvest import trader
 import unittest
 
 from harvest.algo import BaseAlgo 
@@ -44,6 +45,17 @@ def test_bbands(self):
         self.assertAlmostEqual(middle[-1], expected_middle, places=5)
         self.assertAlmostEqual(upper[-1], expected_middle + std, places=5)
         self.assertAlmostEqual(lower[-1], expected_middle - std, places=5)
+    
+    def test_bbands_trader(self):
+        t = trader.Trader()
+        t.set_symbol('DUMMY')
+        t.set_algo(BaseAlgo())
+        t.start("1MIN", kill_switch=True)
+
+        upper, middle, lower = t.algo[0].bbands()
+
+        self.assertEqual(True, True)
+
 
 if __name__ == '__main__':
     unittest.main()
\ No newline at end of file
diff --git a/test/test_dummy_streamer.py b/test/test_dummy_streamer.py
new file mode 100644
index 00000000..c3388bfb
--- /dev/null
+++ b/test/test_dummy_streamer.py
@@ -0,0 +1,37 @@
+# Builtins
+import pathlib
+import unittest
+import datetime as dt
+
+from harvest.api.dummy import DummyStreamer 
+
+class TestDummyStreamer(unittest.TestCase):
+    def test_fetch_prices(self):
+        dummy = DummyStreamer()
+        df = dummy.fetch_price_history('PO', '1HR', dt.datetime.now() - dt.timedelta(hours=50), dt.datetime.now())['PO']
+        self.assertEqual(list(df.columns.values), ['open', 'high', 'low', 'close', 'volume'])
+
+    def test_setup(self):
+        dummy = DummyStreamer()
+        watch = ['A', 'B', 'C', '@D']
+        dummy.setup(watch, '1MIN')
+        self.assertEqual(dummy.watch, watch)
+
+    def test_get_stock_price(self):
+        dummy = DummyStreamer()
+        watch = ['A', 'B', 'C', '@D']
+        dummy.setup(watch, '1MIN')
+        d = dummy.fetch_latest_stock_price()
+        self.assertEqual(len(d), 3)
+
+    def test_get_crypto_price(self):
+        dummy = DummyStreamer()
+        watch = ['A', 'B', 'C', '@D']
+        dummy.setup(watch, '1MIN')
+        d = dummy.fetch_latest_crypto_price()
+        self.assertTrue('@D' in d)
+        self.assertEqual(d['@D'].shape, (1, 5))
+
+
+if __name__ == '__main__':
+    unittest.main()
\ No newline at end of file
diff --git a/test/test_dummy_broker.py b/test/test_paper_broker.py
similarity index 73%
rename from test/test_dummy_broker.py
rename to test/test_paper_broker.py
index 2a68219c..954d4c0c 100644
--- a/test/test_dummy_broker.py
+++ b/test/test_paper_broker.py
@@ -3,47 +3,24 @@
 import unittest
 import datetime as dt
 
-from harvest.broker.dummy import DummyBroker 
-
-class TestDummyBroker(unittest.TestCase):
-    def test_fetch_prices(self):
-        dummy = DummyBroker()
-        df = dummy.fetch_price_history('PO', '1HR', dt.datetime.now() - dt.timedelta(hours=50), dt.datetime.now())['PO']
-        self.assertEqual(list(df.columns.values), ['open', 'high', 'low', 'close', 'volume'])
-
-    def test_setup(self):
-        dummy = DummyBroker()
-        watch = ['A', 'B', 'C', '@D']
-        dummy.setup(watch, '1MIN')
-        self.assertEqual(dummy.watch, watch)
-
-    def test_get_stock_price(self):
-        dummy = DummyBroker()
-        watch = ['A', 'B', 'C', '@D']
-        dummy.setup(watch, '1MIN')
-        d = dummy.fetch_latest_stock_price()
-        self.assertEqual(len(d), 3)
-
-    def test_get_crypto_price(self):
-        dummy = DummyBroker()
-        watch = ['A', 'B', 'C', '@D']
-        dummy.setup(watch, '1MIN')
-        d = dummy.fetch_latest_crypto_price()
-        self.assertTrue('@D' in d)
-        self.assertEqual(d['@D'].shape, (1, 5))
+from harvest.api.paper import PaperBroker 
+from harvest.api.dummy import DummyStreamer
+
+class TestPaperBroker(unittest.TestCase):
 
     def test_account(self):
-        dummy = DummyBroker()
+        dummy = PaperBroker()
+        dummy.streamer = DummyStreamer()
         d = dummy.fetch_account()
-        self.assertEqual(d['equity'], 100000.0)
-        self.assertEqual(d['cash'], 100000.0)
-        self.assertEqual(d['buying_power'], 100000.0)
+        self.assertEqual(d['equity'], 1000000.0)
+        self.assertEqual(d['cash'], 1000000.0)
+        self.assertEqual(d['buying_power'], 1000000.0)
         self.assertEqual(d['multiplier'], 1)
-        
 
     def test_dummy_account(self):
         directory = pathlib.Path(__file__).parent.resolve()
-        dummy = DummyBroker(str(directory) + '/../dummy_account.yaml')
+        dummy = PaperBroker(str(directory) + '/../dummy_account.yaml')
+        dummy.streamer = DummyStreamer()
         stocks = dummy.fetch_stock_positions()
         self.assertEqual(len(stocks), 2)
         self.assertEqual(stocks[0]['symbol'], 'A')
@@ -57,7 +34,8 @@ def test_dummy_account(self):
         self.assertEqual(cryptos[0]['quantity'], 2)
 
     def test_buy_order_limit(self):
-        dummy = DummyBroker() 
+        dummy = PaperBroker() 
+        dummy.streamer = DummyStreamer()
         dummy.setup(['A'], '1MIN')
         order = dummy.order_limit('buy', 'A', 5, 25)
         self.assertEqual(order['type'], 'STOCK')
@@ -74,7 +52,8 @@ def test_buy_order_limit(self):
         self.assertEqual(status['status'], 'filled')
 
     def test_buy(self):
-        dummy = DummyBroker() 
+        dummy = PaperBroker() 
+        dummy.streamer = DummyStreamer()
         dummy.setup(['A'], '1MIN')
         order = dummy.buy('A', 5)
         self.assertEqual(order['type'], 'STOCK')
@@ -92,7 +71,8 @@ def test_buy(self):
 
     def test_sell_order_limit(self):
         directory = pathlib.Path(__file__).parent.resolve()
-        dummy = DummyBroker(str(directory) + '/../dummy_account.yaml') 
+        dummy = PaperBroker(str(directory) + '/../dummy_account.yaml') 
+        dummy.streamer = DummyStreamer()
         dummy.setup(['A'], '1MIN')
         order = dummy.order_limit('sell', 'A', 2, 3)
         self.assertEqual(order['type'], 'STOCK')
@@ -110,7 +90,8 @@ def test_sell_order_limit(self):
 
     def test_sell(self):
         directory = pathlib.Path(__file__).parent.resolve()
-        dummy = DummyBroker(str(directory) + '/../dummy_account.yaml')
+        dummy = PaperBroker(str(directory) + '/../dummy_account.yaml')
+        dummy.streamer = DummyStreamer()
         dummy.setup(['A'], '1MIN')
         order = dummy.sell('A', 2)
         self.assertEqual(order['type'], 'STOCK')
@@ -127,7 +108,8 @@ def test_sell(self):
         self.assertEqual(status['status'], 'filled')
 
     def test_order_option_limit(self):
-        dummy = DummyBroker() 
+        dummy = PaperBroker() 
+        dummy.streamer = DummyStreamer()
         dummy.setup(['A'], '1MIN')
         exp_date = dt.datetime.now() + dt.timedelta(hours=5)
         order = dummy.order_option_limit('buy', 'A', 5, 25.75, 'OPTION', exp_date, 31.25)
diff --git a/test/test_rh_broker.py b/test/test_rh_broker.py
index 87bf2481..4664e4d1 100644
--- a/test/test_rh_broker.py
+++ b/test/test_rh_broker.py
@@ -1,5 +1,5 @@
 import unittest
-from harvest import trader, broker, algo 
+from harvest import trader, api, algo 
 
 class TestRHBroker(unittest.TestCase):
     def test_trade(self):
@@ -9,4 +9,6 @@ def test_trade(self):
         pass
 
 if __name__ == '__main__':
-    unittest.main()
\ No newline at end of file
+    unittest.main()
+
+
diff --git a/test/test_tester.py b/test/test_tester.py
index f142b067..7074ddb4 100644
--- a/test/test_tester.py
+++ b/test/test_tester.py
@@ -3,12 +3,12 @@
 # Submodule imports
 from harvest.trader import BackTester
 from harvest.algo import BaseAlgo
-from harvest.broker.dummy import DummyBroker
+from harvest.api.dummy import DummyStreamer
 
 class TestTester(unittest.TestCase):    
 
     def test_tester_adding_symbol(self):
-        dummy_broker = DummyBroker()
+        dummy_broker = DummyStreamer()
         t = BackTester(dummy_broker)
         t.set_symbol('A')
         self.assertEqual(t.watch[0], 'A')
@@ -18,7 +18,7 @@ def test_start_do_nothing(self):
         to ensure it can run without crashing.
         """
         t = BackTester()
-        t.add_symbol('A')
+        t.set_symbol('A')
         t.set_algo(BaseAlgo())
         t.start('1MIN', ['5MIN', '30MIN', '1DAY'], source='FETCH', kill_switch=True)
         self.assertTrue(True)
@@ -27,7 +27,7 @@ def test_check_aggregation(self):
         """ 
         """
         t = BackTester()
-        t.add_symbol('A')
+        t.set_symbol('A')
         t.set_algo(BaseAlgo())
         t.start('1MIN', ['1DAY'], source='FETCH', kill_switch=True)
         
diff --git a/test/test_trader.py b/test/test_trader.py
index bd563292..c81f1f34 100644
--- a/test/test_trader.py
+++ b/test/test_trader.py
@@ -3,23 +3,30 @@
 # Submodule imports
 from harvest import trader
 from harvest.algo import BaseAlgo
-from harvest.broker.dummy import DummyBroker
+from harvest.api.dummy import DummyStreamer
 
 class TestTrader(unittest.TestCase):    
     def test_trader_adding_symbol(self):
-        dummy_broker = DummyBroker()
-        t = trader.Trader(dummy_broker)
+        t = trader.Trader()
         t.set_symbol('A')
         self.assertEqual(t.watch[0], 'A')
 
     def test_start_do_nothing(self):
-        dummy_broker = DummyBroker()
-        t = trader.Trader(dummy_broker)
-        t.add_symbol('A')
+        t = trader.Trader()
+        t.set_symbol('A')
         t.set_algo(BaseAlgo())
         t.start('1MIN', kill_switch=True)
-        self.assertTrue(True)
 
+        self.assertTrue(True)
+    
+    def test_no_streamer(self):
+        """If streamer is not specified, by default
+        it should be set to DummyStreamer"""
+        t = trader.Trader()
+        t.set_symbol('A')
+        t.set_algo(BaseAlgo())
+        
+        self.assertIsInstance(t.streamer, DummyStreamer)
 
 if __name__ == '__main__':
     unittest.main()
\ No newline at end of file