tamuseanmiller / alpaca-stock-trading-bot

📈 Q-Learning and Sentiment Analysis Stock Trade Bot Powered by Alpaca
GNU General Public License v3.0
42 stars 13 forks source link

Amount of shares #2

Closed tamuseanmiller closed 3 years ago

tamuseanmiller commented 4 years ago

Work on setting up the amount of shares to buy or sell.

GitHubEmploy commented 3 years ago

I think this can be closed now

tamuseanmiller commented 3 years ago

I think this can be closed now

I'm still looking to implement the bot buying or selling x number of shares rather than just one

GitHubEmploy commented 3 years ago

okay, I think I have a solution for you, it takes the buying power, divides it by 4, then divides it by the stock price, and buys that amount. And for selling, it accesses the API and sells all available portions of that Quantity. Although you will have to fashion the code by yourself. Another idea is to take the confidence factor like, 70% confidence on stock, then take the persons buying power, divide by 2, and multiply by risk factor, divide by 100, and buy that amount of stocks. BTW, i just took this from another stock AI that i made, so you wil have to manipulate it into your own code soomehow. buy function:

def buy(self):
        Stock.owned.append(self)
        try:
            global now
            global current_time
            global bbbpower
            now = datetime.now()
            current_time = now.strftime("%H")
            account = Stock._api.get_account()
            perstockpower = int(float(account.buying_power)/4)
            bbbpower = int(perstockpower/float(si.get_live_price(self.symbol)))
            if 14<= int(current_time) <16:
                if int(round(float(si.get_live_price(self.symbol)))) <= perstockpower:
                    logger.info('Bought ' + self.symbol)
                    Stock._api.submit_order(
                        symbol=self.symbol,
                        qty=bbbpower,
                        side='buy',
                        type='market',
                        time_in_force='gtc',
                        extended_hours= True)
                elif int(round(float(si.get_live_price(self.symbol)))) <= perstockpower:
                    logger.info('Bought ' + self.symbol)
                    Stock._api.submit_order(
                        symbol=self.symbol,
                        qty=1,
                        side='buy',
                        type='market',
                        time_in_force='gtc',
                        extended_hours= True)
                else:
                    logger.error('Internal Error in Line 99, python/stock.py')
            else:
                if int(round(float(si.get_live_price(self.symbol)))) <= perstockpower:
                    logger.info ('Bought ' + self.symbol)
                    Stock._api.submit_order(
                        symbol=self.symbol,
                        qty=bbbpower,
                        side='buy',
                        type='market',
                        time_in_force='gtc')
                elif int(round(float(si.get_live_price(self.symbol)))) >= perstockpower:
                    logger.info ('Bought ' + self.symbol)
                    Stock._api.submit_order(
                        symbol=self.symbol,
                        qty=1,
                        side='buy',
                        type='market',
                        time_in_force='gtc')
                else:
                    logger.error('Internal Error in Line 112, python/stock.py')
        except ValueError:
            print('Insufficient Buying Power For Stock', self.symbol + " sorry...")
        return bbbpower

the Sell Function:

def sell(self):
        Stock.owned.remove(self)
        print('Sold ' + self.symbol+" At time "+ current_time)
        position = Stock._api.get_position(self.symbol)
        print(position)
        Stock._api.submit_order(
            symbol=self.symbol,
            qty=position.qty,
            side='sell',
            type='market',
            time_in_force='gtc')

Ask if there are any other questions, ask me. PS. _api is the API, and self.stock is the stock we are working with.

GitHubEmploy commented 3 years ago

I would implement it myself, but I can't seem to figure out where you are using the function to buy and sell only 1 stock. If you can give me the file and line number where you are doing this, I would be more than happy to go and do it myself and send the updated code back

GitHubEmploy commented 3 years ago

Okay, I think I did it. eval.py, line 190 - 205

            if t == data_length - 1:
                file = open('data/' + stock + "_trading_data.csv", 'a')
                file.write(str(datetime.datetime.now().strftime("%m/%d/%Y,%H:%M:%S")) + ',BUY,$' + str(
                    date.get(stock)[0].c) + '\n')
                file.close()
                global now
                global current_time
                global bbbpower
                now = datetime.now()
                current_time = now.strftime("%H")
                account = api.get_account()
                perstockpower = int(float(account.buying_power)/4)
                bbbpower = int(perstockpower/float(si.get_live_price(stock)))
                orders.append(submit_order_helper(bbbpower, stock, 'buy', api))

eval.py line 230 -240

# Sell's one stock using Alpaca's API if it is in realtime
            if t == data_length - 1:
                file = open('data/' + stock + "_trading_data.csv", 'a')
                file.write(str(datetime.datetime.now().strftime("%m/%d/%Y,%H:%M:%S")) + ',SELL,$' + str(
                    date.get(stock)[0].c) + '\n')
                file.close()
                position = api.get_position(stock)
                submit_order_helper(int(position.qty), stock, 'sell', api)
            history.append((data[t], "SELL"))
            if debug:
                logging.debug("Sell at: {} | Sentiment: {} | Position: {}".format(
                    format_currency(data[t]), format_sentiment(sentiments), format_position(data[t] - bought_price)))
                # format_currency(data[t]), format_position(data[t] - bought_price)))

Full updated eval.py file...

"""
Script for evaluating Stock Trading Bot.

Usage:
  eval.py <eval-stock> [--window-size=<window-size>] [--model-name=<model-name>] [--run-bot=<y/n] [--stock-name=<stock-ticker>] [--natural-lang] [--debug]

Options:
  --window-size=<window-size>   Size of the n-day window stock data representation used as the feature vector. [default: 10]
  --model-name=<model-name>     Name of the pretrained model to use (will eval all models in `models/` if unspecified).
  --run-bot=<y/n>               Whether you wish to run the trading bot
  --stock-name=<stock-ticker>   The name of the stock (eg. AMD, GE)
  --natural-lang                Specifies whether to use Google's Natural Language API or not
  --debug                       Specifies whether to use verbose logs during eval operation.
"""

import os
os.environ['TF_MIN_GPU_MULTIPROCESSOR_COUNT'] = '2'
os.environ['APCA_API_BASE_URL'] = 'https://paper-api.alpaca.markets'
os.environ['APCA_API_KEY_ID'] = 'PKVQCPLCNIT0LP3PCG01'
os.environ['APCA_API_SECRET_KEY'] = '24F86p4D6CjNw0YEL4ZtcoiTpeSepcJxXccfsZH5'
os.environ['GOOGLE_APPLICATION_CREDENTIALS'] = 'C:/Users/Mohit/PycharmProjects/SuperAIPR/newslstmstockbot/keys.json'
import coloredlogs

from docopt import docopt

import logging

from yahoo_fin import stock_info as si

import numpy as np

from tqdm import tqdm
from time import process_time

from trading_bot.agent import Agent
from trading_bot.methods import evaluate_model
import alpaca_trade_api as tradeapi
import time
import datetime

from trading_bot.sentiment import runNewsAnalysis, decide_stock
from trading_bot.utils import (
    get_stock_data,
    format_currency,
    format_position,
    show_eval_result,
    switch_k_backend_device,
    format_sentiment)

from trading_bot.ops import get_state

# Method to run script for each minute
def decisions(agent, data, window_size, debug, stock, api):
    # Initialize Variables
    total_profit = 0
    global orders
    orders = []
    history = []
    agent.inventory = []
    action = None
    sentiments = runNewsAnalysis(stock, api, natural_lang)
    state = get_state(data, 0, window_size + 1)

    # decide_stock()
    t = 0

    # Main While Loop
    while True:

        data_length = len(data) - 1
        is_open = True

        # Checks for if the original 1000 data points were tested
        if t == data_length - 1:

            # Check for connection errors and retry 30 times
            cnt = 0
            while cnt <= 30:

                try:
                    # Wait for market to open.
                    is_open = api.get_clock().is_open
                    break

                except:
                    logging.warning("Error in checking market status, retrying in 30s (" + str(cnt) + "/30)")
                    time.sleep(30)
                    cnt += 1
                    continue

        # Checks for if Market is open
        while not is_open:
            logging.info("Waiting for market to open...")

            # Check for connection errors and retry 30 times
            cnt = 0
            while cnt <= 30:
                try:
                    clock = api.get_clock()
                    opening_time = clock.next_open.replace(tzinfo=datetime.timezone.utc).timestamp()
                    curr_time = clock.timestamp.replace(tzinfo=datetime.timezone.utc).timestamp()
                    time_to_open = int((opening_time - curr_time) / 60)
                    logging.info("Last days profit: {}".format(format_currency(str(total_profit))))

                    # Countdown timer until market opens
                    while time_to_open > -1:
                        print(str(time_to_open) + " minutes til market open.", end='\n')
                        time.sleep(60)
                        time_to_open -= 1

                    # Alternative timer here
                    # time.sleep(time_to_open *    60)
                    is_open = api.get_clock().is_open
                    break

                except:
                    logging.warning("Error in checking market status, retrying in 30s (" + str(cnt) + "/30)")
                    time.sleep(30)
                    cnt += 1
                    continue

            # Initialization of new day, we only want this to happen once at the beginning of each day
            if is_open:
                logging.info("Market opened.")

                # Runs Analysis on all new sources
                try:
                    sentiments = runNewsAnalysis(stock, api, natural_lang)
                except:
                    logging.info("Error Collecting Sentiment")

                # Save last days data
                if action is not None:
                    agent.memory.append((state, action, reward, next_state, True))
                    agent.soft_save()

                # Reinitialize for new day
                total_profit = 0
                orders = []
                history = []
                q = 0
                agent.inventory = []

                # ****COMMENT THIS OUT IF YOU DON'T WANT TO SELL ALL OF THE STOCKS AT THE BEGINNING OF NEW DAY****
                # Sell all stock using Alpaca API at the beginning of the new day
                if t == data_length - 1:

                    try:
                        qty = api.get_position(stock).qty

                    except:
                        logging.warning("Error fetching stock position, may not exist.")

                    # Just checks to see if I'm trying to sell zero or a negative number of stocks
                    if int(qty) > 2:
                        submit_order_helper(int(qty) - 2, stock, 'sell', api)

        # Checks for if the original 1000 data points were tested
        if t == data_length - 1:
            time.sleep(60)

            # Check for connection errors and retry 30 times
            cnt = 0
            while cnt <= 30:
                try:
                    date = api.get_barset(timeframe='minute', symbols=stock_name, limit=1, end=datetime.datetime.now())
                    break

                except:
                    logging.warning("Unable to retrieve barset, retrying in 30s (" + str(cnt) + "/30)")
                    time.sleep(30)
                    cnt += 1
                    continue

            data.append(date.get(stock)[0].c)

        reward = 0
        next_state = get_state(data, t + 1, window_size + 1)

        # select an action
        action = agent.act(state, is_eval=True)

        # BUY
        if action == 1 and sentiments >= 0:
            # if action == 1:
            agent.inventory.append(data[t])

            # Buy using Alpaca API, only if it is realtime data
            if t == data_length - 1:
                file = open('data/' + stock + "_trading_data.csv", 'a')
                file.write(str(datetime.datetime.now().strftime("%m/%d/%Y,%H:%M:%S")) + ',BUY,$' + str(
                    date.get(stock)[0].c) + '\n')
                file.close()
                global now
                global current_time
                global bbbpower
                now = datetime.now()
                current_time = now.strftime("%H")
                account = api.get_account()
                perstockpower = int(float(account.buying_power)/4)
                bbbpower = int(perstockpower/float(si.get_live_price(stock)))
                orders.append(submit_order_helper(bbbpower, stock, 'buy', api))

            # Appends and logs
            history.append((data[t], "BUY"))
            if debug:
                logging.debug(
                    "Buy at: {}  | Sentiment: {} | Total Profit: {}".format(format_currency(data[t]),
                                                                            format_sentiment(sentiments),
                                                                            format_currency(total_profit)))
                # "Buy at: {}".format(format_currency(data[t])))

        # SELL
        elif (action == 2 or sentiments < 0) and len(agent.inventory) > 0:
            # elif action == 2 and len(agent.inventory) > 0:
            bought_price = agent.inventory.pop(0)
            reward = max(data[t] - bought_price, 0)
            total_profit += data[t] - bought_price

            # Sell all stock using Alpaca API
            # if t == data_length - 1 and len(orders) != 0:
            #    try:
            #        qty = api.get_position(stock).qty
            #        submit_order_helper(qty, stock, 'sell', api)
            #        orders.pop()
            #    except:
            #        logging.info("No position!")

            # Sell's one stock using Alpaca's API if it is in realtime
            if t == data_length - 1:
                file = open('data/' + stock + "_trading_data.csv", 'a')
                file.write(str(datetime.datetime.now().strftime("%m/%d/%Y,%H:%M:%S")) + ',SELL,$' + str(
                    date.get(stock)[0].c) + '\n')
                file.close()
                position = api.get_position(stock)
                submit_order_helper(int(position.qty), stock, 'sell', api)
            history.append((data[t], "SELL"))
            if debug:
                logging.debug("Sell at: {} | Sentiment: {} | Position: {}".format(
                    format_currency(data[t]), format_sentiment(sentiments), format_position(data[t] - bought_price)))
                # format_currency(data[t]), format_position(data[t] - bought_price)))

        # HOLD
        else:
            history.append((data[t], "HOLD"))
            if debug:
                logging.debug("Hold at: {} | Sentiment: {} | Total Profit: {}".format(
                    format_currency(data[t]), format_sentiment(sentiments), format_currency(total_profit)))
                # format_currency(data[t])))

            if t == data_length - 1:
                file = open('data/' + stock + "_trading_data.csv", 'a')
                file.write(str(datetime.datetime.now().strftime("%m/%d/%Y,%H:%M:%S")) + ',HOLD,$' + str(
                    date.get(stock)[0].c) + '\n')
                file.close()

        agent.memory.append((state, action, reward, next_state, False))
        if len(agent.memory) > 32:
            agent.train_experience_replay(32)

        state = next_state
        t += 1

# Submit an order if quantity is above 0.
def submit_order_helper(qty, stock, side, api):
    if int(qty) > 0:
        try:
            api.submit_order(stock, qty, side, "market", "day")
            logging.info("Market order of | " + str(qty) + " " + stock + " " + side + " | completed.")
        except:
            logging.warning("Order of | " + str(qty) + " " + stock + " " + side + " | did not go through.")
    else:
        logging.info("Quantity is 0, order of | " + str(qty) + " " + stock + " " + side + " | not completed.")

# Method to actually run the script
def alpaca_trading_bot(stock_name, window_size=10, model_name='model_debug'):
    # Alpaca API
    api = tradeapi.REST()

    # Create Agent Object
    agent = Agent(window_size, pretrained=True, model_name=model_name)

    # Get Ticker from last intraday times from Polygon
    file = open('ticker.csv', 'w')
    file.write('Adj Close\n')

    # Check for connection errors and retry 30 times
    cnt = 0
    while cnt <= 30:
        try:
            date = api.get_barset(timeframe='minute', symbols=stock_name, limit=1000, end=datetime.datetime.now())
            break

        except:
            logging.warning("Error retrieving initial 1000 prices, retrying in 30s (" + str(cnt) + "/30)")
            time.sleep(30)
            cnt += 1
            continue

    # Write ticker csv
    for minutes in date.get(stock_name):
        file.write(str(minutes.c))
        file.write('\n')

    file.close()

    data = get_stock_data('ticker.csv')

    # Call actual buy/sell/hold decisions and print result forever
    decisions(agent, data, window_size, debug, stock_name, api)

def main(eval_stock, window_size, model_name, debug):
    """ Evaluates the stock trading bot.
    Please see https://arxiv.org/abs/1312.5602 for more details.
    Args: [python eval.py --help]
    """
    data = get_stock_data(eval_stock)
    initial_offset = data[1] - data[0]

    # Single Model Evaluation
    if model_name is not None:
        agent = Agent(window_size, pretrained=True, model_name=model_name)
        profit, _ = evaluate_model(agent, data, window_size, debug)
        show_eval_result(model_name, profit, initial_offset)

    # Multiple Model Evaluation
    else:
        for model in os.listdir("models"):
            if os.path.isfile(os.path.join("models", model)):
                agent = Agent(window_size, pretrained=True, model_name=model)
                profit = evaluate_model(agent, data, window_size, debug)
                show_eval_result(model, profit, initial_offset)
                del agent

if __name__ == "__main__":
    args = docopt(__doc__)

    # Arguments
    eval_stock = 'data/' + str(args["<eval-stock>"]) + '.csv'
    window_size = int(args["--window-size"])
    model_name = args["--model-name"]
    run_bot = str(args['--run-bot'])
    stock_name = str(args['--stock-name'])
    natural_lang = args['--natural-lang']
    debug = args["--debug"]

    coloredlogs.install(level="DEBUG")
    switch_k_backend_device()

    try:
        main(eval_stock, window_size, model_name, debug)
    except KeyboardInterrupt:
        print("Aborted")

    # Run the Actual bot
    if run_bot == 'y' or run_bot == 'Y':
        alpaca_trading_bot(stock_name, window_size, model_name)
tamuseanmiller commented 3 years ago

I do like the idea for buying quite a bit, i think it may be worth looking more at how many stocks are sold, may still work though. Why don't you add it to your other pull request and I'll take a look at both?

GitHubEmploy commented 3 years ago

Okay, I already added the other, and I'm in the process of doing this one

tamuseanmiller commented 3 years ago

Also be careful with your credentials.

GitHubEmploy commented 3 years ago

Yes, I just noticed and I am deleting those from the files... whoops.

GitHubEmploy commented 3 years ago

Ok, I fixed the problem, can u please accept my pull request?

GitHubEmploy commented 3 years ago

Okay, so believe I have given you time to look over my files and read them. I can also confirm that my system works as I tested it out today, and it trader just fine. Although, you will have to change the training algo in correspondence with these new changes.