コード例 #1
0
 def open_position(self):
     # do not buy if we're out of funds!
     budget = self.buying_power.spend_and_get_amount()
     if budget < self.BUDGET_THRESHHOLD:
         # don't make trades for under a certain threshhold
         return
     if self.paper_trading:
         # if the order timed out or was rejected for some other reason
         # then try again when next relevant
         try:
             self.position = OpenPaperPosition(self.ticker, budget, self.market_data)
         except TraderbotException as te:
             print_with_lock("open position exception:", str(te))
             self.position = None
             return
     else:
         try:
             self.position = OpenStockPosition(self.ticker, budget)
         except TraderbotException as te:
             print_with_lock("open position exception:", str(te))
             self.position = None
             return
     
     # update statistics
     self.statistics.append({
         "open_time": datetime.now(),
         "quantity": self.position.get_quantity(),
         "open_price": self.position.get_open_price(),
         "close_time": -1,
         "close_price": -1
     })
コード例 #2
0
ファイル: position.py プロジェクト: michaeljecmen/traderbot
    def monitor_order(self, resp, ticker):
        """Monitors the order with the given ID until it is filled or TIMEOUT is exceeded, in which case the order is cancelled.
        Returns the response dictionary or throws, depending on whether or not the order succeeded or failed.
        Checks for fill status twice every second."""
        order_id = resp.get('id', None)
        if order_id is None:
            raise TraderbotException(
                "initial order for {} failed".format(ticker))

        # before waiting check if we already filled it
        if resp['state'] == 'filled':
            return resp

        # then check every half second
        for _ in range(2 * self.TIMEOUT):
            resp = r.orders.get_stock_order_info(order_id)
            if resp['state'] == 'filled':  # TODO orders are being cancelled unexpectedly
                return resp
            if resp['state'] == 'cancelled':
                raise TraderbotException(
                    "order for {} was cancelled unexpectedly: {}".format(
                        ticker, resp))
            time.sleep(0.5)
        # TODO if partially filled here need to tell the caller as much
        # what the actual qty and price are so they can sell accordingly
        # same for when they actually sell. maybe return remaining position
        # so it works for both
        print_with_lock("TIMING OUT BUT DEBUG IN CASE PARTIAL FILL: ", resp)
        resp = r.cancel_stock_order(order_id)
        print_with_lock("debug: response from cancelling stock order: ", resp)
        raise TraderbotException("order for {} timed out".format(ticker))
コード例 #3
0
ファイル: position.py プロジェクト: michaeljecmen/traderbot
 def print_close(self, close_price):
     fqty = self._fnum(self.quantity)
     ftot = self._fnum(self.quantity * close_price)
     fnet = self._fnum((close_price - self.open_price) * self.quantity)
     print_with_lock(
         "sold {} shares of {} (total of ${}) for a net {} of {}".format(
             fqty, self.ticker, ftot,
             "gain" if close_price > self.open_price else "loss", fnet))
コード例 #4
0
 def should_buy_on_tick(self):
     # update both and buy if we have a higher short than long MA
     self.short_moving_avg.update()
     self.long_moving_avg.update()
     print_with_lock("MA for {}: short={} long={}".format(
         self.ticker, self.short_moving_avg.get_moving_average(),
         self.long_moving_avg.get_moving_average()))
     return self.short_moving_avg.get_moving_average(
     ) > self.long_moving_avg.get_moving_average()
コード例 #5
0
    def add_funds(self, amount):
        """Use this when you close a position to add back the funds earned."""
        if not instant:
            # cannot add funds until 2 days later to non instant accounts
            return

        with self.lock.gen_wlock():
            self.buying_power += amount
            print_with_lock("your account now has ${}".format(
                self.buying_power))
コード例 #6
0
ファイル: traderbot.py プロジェクト: michaeljecmen/traderbot
def log_in_to_robinhood():
    # only use mfa login if it is enabled
    mfa_code=None
    if "mfa-setup-code" in CONFIG.keys():
        # gets current mfa code
        totp = pyotp.TOTP(CONFIG["mfa-setup-code"]).now()
        print_with_lock("DEBUG: current mfa code:", totp)
    login = r.login(USERNAME, PASSWORD, mfa_code=mfa_code)
    print_with_lock("logged in as user {}".format(USERNAME))
    return login
コード例 #7
0
ファイル: traderbot.py プロジェクト: michaeljecmen/traderbot
def block_until_market_open():
    """Block until market open.
    
    Does pre-market work as soon as the loop is entered, exactly once."""
    time_until_open = get_time_until_market_open()
    zero_time = timedelta()
    last_print = time_until_open
    while time_until_open > zero_time:
        if last_print - time_until_open > timedelta(hours=1):
            print_with_lock("still waiting until market open. time remaining:", time_until_open)
            last_print = time_until_open
        # update timedelta
        time_until_open = get_time_until_market_open()
    print_with_lock("market is open")
コード例 #8
0
ファイル: traderbot.py プロジェクト: michaeljecmen/traderbot
def block_until_start_trading():
    """Block until the pre-determined time when we will start trading.
    
    Market is open at this time, so statistics can be gathered and 
    updated in this loop."""
    now = datetime.now()
    start_of_day_datetime = datetime(now.year, now.month, now.day, START_OF_DAY.hour, START_OF_DAY.minute, START_OF_DAY.second, START_OF_DAY.microsecond)
    time_until_start_trading = start_of_day_datetime - now
    zero_time = timedelta()
    last_print = time_until_start_trading
    while time_until_start_trading > zero_time:
        if last_print - time_until_start_trading > timedelta(minutes=5):
            print_with_lock("market is open. will start trading in: ", time_until_start_trading)
            last_print = time_until_start_trading
        time_until_start_trading = start_of_day_datetime - datetime.now()
    print_with_lock("beginning trading")
コード例 #9
0
    def close_position(self):
        close_price = 0.0
        try:
            close_price = self.position.close()
        except TraderbotException as te:
            print_with_lock("close position exception:", str(te))
            return
        ts = datetime.now()
        qty = self.position.get_quantity()
        self.buying_power.add_funds(close_price*qty)

        # update statistics
        self.statistics[-1]["close_time"] = ts
        self.statistics[-1]["close_price"] = close_price
        self.net += ((close_price - self.position.get_open_price()) * self.position.get_quantity())
        self.position = None
コード例 #10
0
 def run(self):
     self.init_time = datetime.now()
     print_with_lock("thread {} began".format(self.ticker))
     
     while self.market_time.is_time_left_to_trade():
         self.looking_to_buy()   
         
         # did we leave the looking to buy function because we bought in?
         # or because we ran out of resources? if we ran out, end this thread
         if self.position is None:
             return
         
         # otherwise, we're now looking to sell
         self.looking_to_sell()
     
     # end of the line for us, generate our report
     self.generate_report()
コード例 #11
0
    def __init__(self, market_data, ticker, n, interval='day'):
        with self.ctor_lock:
            DayMovingAverage.market_data = market_data

        # update self with market info, usually pre-market
        self.n = n
        self.current_moving_avg = 0.0
        self.ticker = ticker

        # get data for a year then whittle down
        data_for_last_n_intervals = r.stocks.get_stock_historicals(
            ticker, interval=interval, span='year')

        # get the last n intervals from the previous year
        data_for_last_n_intervals = data_for_last_n_intervals[-n:]

        # data is currently in the following format:
        # a list of n dictionaries with the structure:
        # {
        #       'begins_at': '2021-03-05T00:00:00Z',
        #       'close_price': '3000.460000',
        #       'high_price': '3009.000000',
        #       'interpolated': False,
        #       'low_price': '2881.000100',
        #       'open_price': '3005.000000',
        #       'session': 'reg',
        #       'symbol': 'AMZN',
        #       'volume': 5388551
        # }
        # so we just want the close price to use as our data
        self.sliding_window = [
            float(daily_stats['close_price'])
            for daily_stats in data_for_last_n_intervals
        ]
        self.calculate_moving_average()
        print_with_lock("{} {} {} moving avg: {}".format(
            ticker, n, interval, self.current_moving_avg))

        # important -- update the sliding window with the current price at the end
        # this allows us to update the curr moving average once the market opens
        # by simply replacing the last value in the sliding window with the current
        # market price
        self.sliding_window.append(
            self.market_data.get_data_for_ticker(ticker))
        self.sliding_window = self.sliding_window[1:]
コード例 #12
0
 def print_data(self):
     """Pretty printing for the internal data of this object."""
     print_with_lock("---- MARKET DATA ----")
     for ticker, index in self.tickers_to_indices.items():
         ticker_data = self.data[index]
         with ticker_data.lock.gen_rlock():
             if len(ticker_data.prices) < ticker_data.trend_len:
                 # make a reversed copy of the list
                 data_snippet = ticker_data.prices[::-1]
             else:
                 data_snippet = ticker_data.get_last_k_prices_in_order()
             
             # data will always be nonempty for a ticker
             print_with_lock("{}: {} {}".format(ticker, data_snippet[0], data_snippet))
     print_with_lock("---------------------")
コード例 #13
0
ファイル: reports.py プロジェクト: michaeljecmen/traderbot
 def print_eod_reports(self):
     net = 0.0
     trades = 0
     pp = pprint.PrettyPrinter(indent=4, sort_dicts=False)
     print_with_lock(
         "=============================== EOD REPORTS ==============================="
     )
     for report in self.reports:
         net += report['traderbot net performance']
         trades += report['total trades made']
         pp.pprint(report)
     print_with_lock(
         "==========================================================================="
     )
     print_with_lock(
         "final summary: {} trades made for a net profit of {}".format(
             trades, net))
コード例 #14
0
 def print(self):
     with self.lock.gen_rlock():
         print_with_lock("TICKERDATA: ind={}, prices={}".format(self.ind, self.prices))
コード例 #15
0
ファイル: traderbot.py プロジェクト: michaeljecmen/traderbot
def run_traderbot():
    """Main function for this module.
    
    Spawns a thread for each ticker that trades on that symbol
    for the duration of the day."""
    # get info from config file and log in
    global USERNAME, PASSWORD, PAPER_TRADING, TIME_ZONE
    global START_OF_DAY, END_OF_DAY, TRADE_LIMIT, CONFIG
    CONFIG = get_json_dict()
    USERNAME = CONFIG["username"]
    PASSWORD = CONFIG["password"]
    MAX_LOSS_PERCENT = CONFIG["max-loss-percent"]/100.0
    TAKE_PROFIT_PERCENT = CONFIG["take-profit-percent"]/100.0
    SPEND_PERCENT = CONFIG["spend-percent"]/100.0
    PAPER_TRADING = CONFIG["paper-trading"]
    TIME_ZONE = CONFIG.get("time-zone-pandas-market-calendars", "America/New_York")
    full_start_time_str = CONFIG.get("start-of-day", "09:30") + "={}".format(TIME_ZONE)
    full_end_time_str = CONFIG.get("end-of-day", "16:00") + "={}".format(TIME_ZONE)
    START_OF_DAY = datetime.strptime(full_start_time_str, "%H:%M=%Z").time()
    END_OF_DAY = datetime.strptime(full_end_time_str, "%H:%M=%Z").time()
    TRADE_LIMIT = CONFIG.get("max-trades-per-day", None)
    BUDGET = CONFIG.get("budget", None)
    END_TIME_STR = CONFIG.get("end-time", None)
    START_TIME_STR = CONFIG.get("start-time", None)
    ALPACA_KEY = CONFIG["alpaca-api-key"]
    ALPACA_SECRET_KEY = CONFIG["alpaca-secret-key"]
    STRATEGIES_DICT = CONFIG["strategies"]
    HISTORY_SIZE = CONFIG.get("history-len", 16)
    if not ((HISTORY_SIZE & (HISTORY_SIZE-1) == 0) and HISTORY_SIZE != 0): 
        raise ConfigException("history-len must be a power of two, {} was entered".format(HISTORY_SIZE))
    TREND_SIZE = CONFIG.get("trend-len", 3)
    if TREND_SIZE > HISTORY_SIZE:
        raise ConfigException("trend-len must be less than or equal to history-len")
    IS_INSTANT_ACCT = CONFIG.get("instant", False)

    zero_time = timedelta()

    login = log_in_to_robinhood()

    # get list of unique tickers and enforce legality of strategies kv in the config
    ALL_TICKERS = []
    for st in STRATEGIES_DICT:
        enforce_keys_in_dict(['strategy', 'tickers'], st)
        enforce_strategy_dict_legal(st['strategy'])
        ALL_TICKERS.extend(st['tickers'])
    ALL_TICKERS = list(set(ALL_TICKERS))

    # generate parameters so we don't get flagged
    START_OF_DAY, END_OF_DAY, TRADE_LIMIT = generate_humanlike_parameters()
    datetime_fmt_str = '%H:%M:%S'
    if END_TIME_STR is not None:
        END_OF_DAY = datetime.strptime(END_TIME_STR, datetime_fmt_str).time()
    if START_TIME_STR is not None:
        START_OF_DAY = datetime.strptime(START_TIME_STR, datetime_fmt_str).time()

    print_with_lock("param: will start trading today at:", START_OF_DAY)
    print_with_lock("param: will stop trading today at:", END_OF_DAY)
    print_with_lock("param: will make a maximum of {} trades today".format(TRADE_LIMIT))

    # busy-spin until market open
    block_until_market_open()

    # these variables are shared by each trading thread. they are written by this
    # main traderbot thread, and read by each trading thread individually
    market_data = MarketData(ALL_TICKERS, ALPACA_KEY, ALPACA_SECRET_KEY, HISTORY_SIZE, TREND_SIZE)
    buying_power = BuyingPower(SPEND_PERCENT, IS_INSTANT_ACCT, BUDGET)
    trade_capper = TradeCapper(TRADE_LIMIT)

    # now that market open is today, update EOD for time checking
    now = datetime.now()
    END_OF_DAY = datetime(now.year, now.month, now.day, END_OF_DAY.hour, END_OF_DAY.minute, END_OF_DAY.second, END_OF_DAY.microsecond)
    market_time = MarketTime(END_OF_DAY)
    reports = Reports()

    # spawn thread for each ticker
    threads = []
    for st in STRATEGIES_DICT:
        strategy_dict = st['strategy']
        tickers = st['tickers']
        for ticker in tickers:
            print_with_lock("initializing thread {} with strategy configuration {}".format(ticker, strategy_dict))
            strategy = strategy_factory(strategy_dict, market_data, ticker)
            if not strategy.is_relevant():
                # don't add irrelevant tickers to the threadpool.
                # long term could figure out how to remove this
                # from the market data object too
                continue
            threads.append(TradingThread(ticker, market_data, market_time, buying_power, trade_capper, strategy, reports, TAKE_PROFIT_PERCENT, MAX_LOSS_PERCENT, PAPER_TRADING))

    # busy spin until we decided to start trading
    block_until_start_trading()

    # update before we start threads to avoid mass panic
    market_data.start_stream()
    market_time.update()

    # start all threads
    for t in threads:
        t.start()

    # update the timer in the main thread
    while market_time.is_time_left_to_trade():
        market_time.update()

    # wait for all threads to finish
    for t in threads:
        t.join()
    
    # now pretty print reports
    reports.print_eod_reports()

    # tidy up after ourselves
    r.logout()
    print_with_lock("logged out user {}".format(USERNAME))
コード例 #16
0
ファイル: position.py プロジェクト: michaeljecmen/traderbot
 def print_open(self):
     fqty = self._fnum(self.quantity)
     fop = self._fnum(self.open_price)
     ftot = self._fnum(self.quantity * self.open_price)
     print_with_lock("opened {} position: {} shares at {} for ${}".format(
         self.ticker, fqty, fop, ftot))