from datetime import datetime, timedelta
from time import time
from typing import Dict
from btalib.indicators.cci import cci
from btalib.indicators.obv import obv
from btalib.indicators.rsi import rsi
from google.cloud import storage
import math
import pandas as pd

pd.set_option('mode.chained_assignment', None)
from alpaca_trade_api import REST
from scipy.stats import linregress
import detect_pattern as pattern

# Get Alpaca API key and secret
storage_client = storage.Client()
bucket = storage_client.get_bucket('derek-algo-trading-bucket')
blob = bucket.blob('alpaca-api-key.txt')
api_key = blob.download_as_text()
blob = bucket.blob('alpaca-secret-key.txt')
secret_key = blob.download_as_text()
base_url = 'https://paper-api.alpaca.markets'
api = REST(api_key, secret_key, base_url, 'v2')

bars = api.get_barset('AAPL', '1D', 5)['AAPL']

print(bars)
def select_swing_stocks(date: datetime, position_data: Dict, portfolio_amount: float):
    # Get Alpaca API key and secret
    storage_client = storage.Client()
    bucket = storage_client.get_bucket('derek-algo-trading-bucket')
    blob = bucket.blob('alpaca-api-key.txt')
    api_key = blob.download_as_text()
    blob = bucket.blob('alpaca-secret-key.txt')
    secret_key = blob.download_as_text()
    base_url = 'https://paper-api.alpaca.markets'
    api = REST(api_key, secret_key, base_url, 'v2')

    # Get all stocks
    assets = api.list_assets('active')
    symbols = [asset.symbol for asset in assets if asset.tradable]

    # Display currently held positions
    if position_data:
        print('Current positions:')
    else:
        print('No current positions')
    position_symbols = list(position_data.keys())
    for symbol in position_symbols:
        current_price = float(position_data[symbol]['current_price'])
        entry_price = float(position_data[symbol]['avg_entry_price'])
        current_percent = (current_price - entry_price) / entry_price * 100
        print('{}: {}%'.format(symbol, '%.2f' % current_percent))

    # Get past 1000 days data for all stocks
    data = {}
    print(f'Retrieving {len(symbols)} symbol data...')
    symbols_chunked = list(chunks(list(set(symbols)), 200))
    previous_day = datetime.isoformat(pd.Timestamp(date - timedelta(days=1)))
    for symbol_group in symbols_chunked:
        data_group = api.get_barset(','.join(symbol_group), '1D', end=previous_day, limit=1000).df
        for symbol in symbol_group:
            data[symbol] = data_group[symbol]
    
    buy_df = pd.DataFrame()
    sell_df = pd.DataFrame()
    hold_df = pd.DataFrame()

    # c = 0
    print('Processing daily bars for all stocks...')
    for symbol in data.keys():
        df = pd.DataFrame(data[symbol])
        df = df.loc[df['close'] > 0]
        if symbol not in position_symbols and len(df) >= 1000:
            df['symbol'] = symbol
            df['bullish'] = pattern.detect_bullish_patterns(df)
            buy_df = buy_df.append(df.loc[df.index == df.index.max()])

        elif symbol in position_symbols:
            df['symbol'] = symbol
            df['qty'] = int(position_data[symbol]['qty'])
            df['market_value'] = float(position_data[symbol]['market_value'])

            latest_rsi = rsi(df).df.tail(1)['rsi'][0]
            latest_cci = cci(df).df.tail(1)['cci'][0]
            purchase_price = float(position_data[symbol]['avg_entry_price'])
            latest_price = float(position_data[symbol]['current_price'])
            df['purchase_price'] = purchase_price
            df['latest_price'] = latest_price
            change_pct = (latest_price - purchase_price) / purchase_price * 100
            print(f'\nHolding {symbol}: {round(change_pct, 2)}%')

            # If it has dropped below the stop loss percentage, GET IT OUT
            if change_pct <= -3:
                print(f'Price is below stop loss: SELL')
                sell_df = sell_df.append(df.loc[df.index == df.index.max()])
            
            elif change_pct < 0 and (latest_rsi >= 70 or latest_cci >= 100):
                print(f'Overbought, RSI={round(latest_rsi, 0)}, CCI={round(latest_cci, 0)}: SELL')
                sell_df = sell_df.append(df.loc[df.index == df.index.max()])

            # If price is at/above entry
            # Check if the swing is still swinging
            else:
                print(f'Price ${latest_price} is near/above entry ${purchase_price}')
                obv_df = obv(df).df
                obv_df['obv_ema'] = obv_df['obv'].ewm(span=20).mean()

                # Is the OBV still above it's EMA
                if obv_df.tail(1)['obv'][0] > obv_df.tail(1)['obv_ema'][0]:
                    print('OBV is still above OBV_EMA')
                    slope = linregress(
                        [0, 1, 2],
                        obv_df.tail(3)['obv'].tolist()
                    ).slope
                    if slope > 0:
                        print(f'OBV is increasing with a slope of {slope}: HOLD')
                        hold_df = hold_df.append(df.loc[df.index == df.index.max()])
                    else:
                        print('OBV is above EMA but not increasing: SELL')
                        sell_df = sell_df.append(df.loc[df.index == df.index.max()])
                else:
                    print('OBV is no longer above EMA: SELL')
                    sell_df = sell_df.append(df.loc[df.index == df.index.max()])

            print()

        # c += 1
    #     if c % 100 == 0:
    #         print(f'{c}/{len(data.keys())}')
    # print(f'{c}/{len(data.keys())}\n')
    
    # END SCREENING SECTION
    
    # Consolidate results
    # hold_stocks = hold_df.loc[hold_df.index == hold_df.index.max()]    
    # sell_stocks = sell_df.loc[sell_df.index == sell_df.index.max()]

    portfolio_size = 20
    purchase_size = portfolio_size - len(hold_df)

    # If there's room in the portfolio to buy more
    if purchase_size > 0:
        print(f'We can purchase {purchase_size} new stocks today.')
        # buy_stocks = buy_df.loc[buy_df.index == buy_df.index.max()]
        buy_stocks = buy_df[buy_df['bullish'] != '']
        buy_stocks = buy_stocks.sort_values(by='volume', ascending=False).head(purchase_size)
        holding_value = sum(hold_df['market_value'].tolist()) if not hold_df.empty else 0.0
        available_funds = portfolio_amount - holding_value
        print(f'Available funds to buy: ${available_funds}')
        
        # For now this will create equal-weight positions
        funds_per_stock = available_funds / len(buy_stocks)

        # Calculate quantity to buy for each stock
        buy_qty = []
        symbols = buy_stocks['symbol'].tolist()
        prices = buy_stocks['close'].tolist()
        for i in range(0, len(symbols)):
            price = prices[i]
            qty = math.floor(funds_per_stock / price)
            buy_qty.append(qty)
        
        buy_stocks['qty'] = buy_qty
        buy_stocks['market_value'] = buy_stocks['close'] * buy_stocks['qty']
    
    else:
        print('There is no room to buy.')
        buy_stocks = pd.DataFrame()

    return (buy_stocks, sell_df, hold_df)
Beispiel #3
0
def select_swing_stocks():
    # Get Alpaca API key and secret
    storage_client = storage.Client()
    bucket = storage_client.get_bucket('derek-algo-trading-bucket')
    blob = bucket.blob('alpaca-api-key.txt')
    api_key = blob.download_as_text()
    blob = bucket.blob('alpaca-secret-key.txt')
    secret_key = blob.download_as_text()
    base_url = 'https://paper-api.alpaca.markets'
    api = REST(api_key, secret_key, base_url, 'v2')

    # Get all stocks
    assets = api.list_assets('active')
    symbols = [asset.symbol for asset in assets if asset.tradable]

    # Get all currently held positions
    positions = api.list_positions()
    if positions:
        print('Current positions:')
    position_symbols = [position.symbol for position in positions]
    position_data = {}
    for position in positions:
        current_percent = (float(position.current_price) - float(position.avg_entry_price)) \
            / float(position.avg_entry_price) * 100
        print('{}: {}%'.format(position.symbol, '%.2f' % current_percent))
        position_data[position.symbol] = position
    print()

    # Get past 50 days data for all stocks
    data = {}
    symbols_chunked = list(chunks(list(set(symbols)), 200))
    for symbol_group in symbols_chunked:
        print(f'Retrieving {len(symbol_group)} symbol data')
        data_group = api.get_barset(','.join(symbol_group), '1D',
                                    limit=1000).df
        for symbol in symbol_group:
            data[symbol] = data_group[symbol]

    buy_df = pd.DataFrame()
    sell_df = pd.DataFrame()
    hold_df = pd.DataFrame()

    c = 0
    for symbol in data.keys():
        df = pd.DataFrame(data[symbol])
        df = df.loc[df['close'] > 0]
        if symbol not in position_symbols and len(df) == 1000:
            df['symbol'] = symbol

            # bullish pattern detection
            # bullish = [''] * len(df)
            # bullish[len(bullish) - 1] = pattern.detect_bullish_patterns(df)
            df['bullish'] = pattern.detect_bullish_patterns(df)
            buy_df = buy_df.append(df.loc[df.index == df.index.max()])

        elif symbol in position_symbols:
            print(f'\nCurrently holding {symbol}:')
            df['symbol'] = symbol
            df['qty'] = int(position_data[symbol].qty)
            df['market_value'] = float(position_data[symbol].market_value)

            latest_rsi = rsi(df).df.tail(1)['rsi'][0]
            latest_cci = cci(df).df.tail(1)['cci'][0]
            purchase_price = float(position_data[symbol].avg_entry_price)
            latest_price = float(position_data[symbol].current_price)
            df['purchase_price'] = purchase_price
            df['latest_price'] = latest_price

            # If it has dropped below the entry price, GET IT OUT
            if latest_price < purchase_price:
                print(
                    f'Price ${latest_price} is below entry ${purchase_price}: SELL'
                )
                sell_df = sell_df.append(df.loc[df.index == df.index.max()])
            # Or if the RSI or CCI are too high, GET IT OUT
            elif latest_rsi >= 70 or latest_cci >= 100:
                print(f'Overbought, RSI={latest_rsi}, CCI={latest_cci}: SELL')
                sell_df = sell_df.append(df.loc[df.index == df.index.max()])

            # If price is at/above entry
            # Check if the swing is still swinging
            elif latest_price >= purchase_price:
                print(
                    f'Price ${latest_price} is at/above entry ${purchase_price}'
                )
                obv_df = obv(df).df
                obv_df['obv_ema'] = obv_df['obv'].ewm(span=20).mean()

                # Is the OBV still above it's EMA
                if obv_df.tail(1)['obv'][0] > obv_df.tail(1)['obv_ema'][0]:
                    print('OBV is still above OBV_EMA')
                    slope = linregress([0, 1, 2],
                                       obv_df.tail(3)['obv'].tolist()).slope
                    if slope > 0:
                        print(
                            f'OBV is increasing with a slope of {slope}: HOLD')
                        hold_df = hold_df.append(
                            df.loc[df.index == df.index.max()])

            print()

        c += 1
        if c % 100 == 0:
            print(f'{c}/{len(data.keys())}')
    print(f'{c}/{len(data.keys())}\n')

    # END SCREENING SECTION

    # Consolidate results
    print('DECISION:\n')
    hold_stocks = hold_df.loc[hold_df.index == hold_df.index.max()]
    if not hold_stocks.empty:
        print(f'Hold {len(hold_stocks)}:\n' +
              '\n'.join(hold_stocks['symbol'].tolist()) + '\n')

    sell_stocks = sell_df.loc[sell_df.index == sell_df.index.max()]
    if not sell_stocks.empty:
        print(f'Sell {len(sell_stocks)}:\n' +
              '\n'.join(sell_stocks['symbol'].tolist()) + '\n')

    portfolio_size = 20
    purchase_size = portfolio_size - len(hold_stocks)

    # If there's room in the portfolio to buy more
    if purchase_size > 0:
        print(f'We can purchase {purchase_size} new stocks today.')
        buy_stocks = buy_df.loc[buy_df.index == buy_df.index.max()]
        buy_stocks = buy_stocks[buy_stocks['bullish'] != '']
        buy_stocks = buy_stocks.sort_values(
            by='volume', ascending=False).head(purchase_size)

        account = api.get_account()
        holding_value = hold_stocks['market_value'].tolist().sum(
        ) if not hold_stocks.empty else 0.0
        available_funds = (float(account.portfolio_value) +
                           float(account.cash) - holding_value) * 0.95
        print(f'Available funds to buy: ${available_funds}')

        # For now this will create equal-weight positions
        funds_per_stock = available_funds / len(buy_stocks)

        # Calculate quantity to buy for each stock
        buy_qty = []
        symbols = buy_stocks['symbol'].tolist()
        prices = buy_stocks['close'].tolist()
        for i in range(0, len(symbols)):
            price = prices[i]
            qty = math.floor(funds_per_stock / price)
            buy_qty.append(qty)

        buy_stocks['qty'] = buy_qty
        buy_stocks['market_value'] = buy_stocks['close'] * buy_stocks['qty']
        print(f'Buy {len(buy_stocks)}:')
        print(buy_stocks)

    else:
        print('There is no room to buy.')
        buy_stocks = pd.DataFrame()

    return (buy_stocks, sell_stocks, hold_stocks)
Beispiel #4
0
def select_day_trade_stocks(event, context):
    # Get Alpaca API key and secret
    storage_client = storage.Client()
    bucket = storage_client.get_bucket('derek-algo-trading-bucket')
    blob = bucket.blob('alpaca-api-key.txt')
    api_key = blob.download_as_text()
    blob = bucket.blob('alpaca-secret-key.txt')
    secret_key = blob.download_as_text()
    base_url = 'https://paper-api.alpaca.markets'
    api = REST(api_key, secret_key, base_url, 'v2')

    # Get all stocks
    assets = api.list_assets('active')
    symbols = [asset.symbol for asset in assets if asset.tradable]

    # Get past 50 days data for all stocks
    data = {}
    symbols_chunked = list(chunks(list(set(symbols)), 200))
    for symbol_group in symbols_chunked:
        print(f'Retrieving {len(symbol_group)} symbol data')
        data_group = api.get_barset(','.join(symbol_group), '1D', limit=50).df
        for symbol in symbol_group:
            data[symbol] = data_group[symbol]
    
    result_df = pd.DataFrame()

    for symbol in data.keys():
        df = data[symbol]
        df = df.loc[df['close'] > 0]
        if len(df.index) >= 50:
            # Calculate day change percent
            dayChange = []
            for i in df.index:
                try:
                    high = df['high'][i]
                    low = df['low'][i]
                    if low and low > 0:
                        dayChange.append(round(((high - low) / low) * 100, 2))
                    else:
                        dayChange.append(0)
                except KeyError:
                    dayChange.append(0)

            # Calculate averages
            df['dayChange'] = dayChange
            df['averageDayChange10'] = df['dayChange'].ewm(span=10).mean()
            df['averageVolume50'] = df['volume'].ewm(span=50).mean()
            df['averageVolume10'] = df['volume'].ewm(span=10).mean()
            df['symbol'] = symbol
    
            # Add most recent row
            i = len(df.index) - 1
            result_df = result_df.append(df.iloc[i])
    
    # Filter stocks by high volatility criteria
    filtered_df = result_df.query(
        'averageDayChange10>4.5 & averageVolume50>4000000 & averageVolume10>4000000 & close>=5'
    )

    # Sort descending by averageDayChange10
    sorted_df = filtered_df.sort_values('averageDayChange10', ascending=False)

    # Grab top 5 stocks
    selected_stocks = sorted_df[:5]
    print(selected_stocks)
    symbols = list(selected_stocks['symbol'].values)
    print(symbols)

    # Store them in Alpaca watchlist
    watchlist = api.get_watchlist_by_name('day-trade-stocks')
    api.update_watchlist(watchlist.id, symbols=symbols)
    print('Success')