Beispiel #1
0
def momentum(stock_prices,
             cool_down_period=3,
             period=7,
             osc_type='stochastic',
             amount=5000,
             fees=20,
             ledger='ledger_momentum.txt'):
    '''
    Oscillators can help us guess if the price of a share is currently overvalued (overbought) or undervalued (oversold). Generally:

    the price is considered overvalued when the oscillator is above a threshold of 0.7 to 0.8 (good time to sell).
    the price is considered undervalued when the oscillator is below a threshold of 0.2 to 0.3 (good time to buy).

    '''
    day = period
    stock_list = stock_prices[0, ]
    portfolio = proc.create_portfolio([5000] * len(stock_list), stock_prices,
                                      fees)
    valued = np.array([
        oscillator(stock_prices[:, i], period, osc_type)
        for i in range(len(stock_list))
    ])
    while day < (stock_prices.shape[0] - period):
        if day != 0 and (portfolio == np.zeros(len(portfolio))).all():
            break
        for stock in range(len(stock_list)):
            # When the FMA crosses the SMA from below,buy
            if valued[stock][day] > 0.2 and valued[stock][day] < 0.3:
                proc.buy(day, stock, amount, stock_prices, fees, portfolio,
                         ledger)
            elif valued[stock][day] > 0.7 and valued[stock][day] < 0.8:
                proc.sell(day, stock, stock_prices, fees, portfolio, ledger)
        day += cool_down_period
def plot_indicators(stock_price, days, sma_window, fma_window, rsi_period,
                    stoch_period):
    fig, axes = plt.subplots(2, 1, sharex=True, figsize=(20, 10))
    plt.setp(axes, xlim=(sma_window, days))

    # Getting smas and fmas
    smas = indi.moving_average(stock_price, window_size=sma_window)
    fmas = indi.moving_average(
        stock_price, window_size=fma_window)[sma_window - fma_window:]
    # Comparison from day = sma_period - fma_period
    deltas = fmas - smas
    # Find buying and selling days
    cross_days = np.where((np.diff(np.sign(deltas)) != 0))[0] + 1
    ma_sell_days = []
    ma_buy_days = []
    for cross_day in cross_days:
        if (fmas[cross_day - 1] > smas[cross_day - 1]):
            ma_sell_days.append(cross_day + sma_window)
        else:
            ma_buy_days.append(cross_day + sma_window)

    # Computing oscillator values
    rsis = indi.oscillator(stock_price, n=rsi_period,
                           osc_type='rsi')[sma_window - 1:]
    stochs = indi.oscillator(stock_price,
                             n=stoch_period,
                             osc_type='stochastic')[sma_window - 1:]

    # Plotting MA Comparison
    days = np.arange(sma_window, days + 1)
    axes[0].set_title('MA Comparison')
    for buy_day in ma_buy_days:
        axes[0].axvline(x=buy_day, color='g', linestyle='--')
    for sell_day in ma_sell_days:
        axes[0].axvline(x=sell_day, color='r', linestyle='--')
    axes[0].plot(days, stock_price[sma_window - 1:], 'y-', label='Stock Price')
    axes[0].plot(days, smas, 'c-', label=f'{sma_window}-day SMA')
    axes[0].plot(days, fmas, 'm-', label=f'{fma_window}-day FMA')
    axes[0].legend()

    # Plotting Oscillator Comparison
    axes[1].set_title('Oscillator Comparison')
    axes[1].plot(days, rsis, 'r-', label=f'{rsi_period}-day RSI')
    axes[1].plot(days, stochs, 'b-', label=f'{stoch_period}-day Stochastic')
    axes[1].legend()
    plt.savefig('indicators.png')
def momentum(stock_prices,
             osc_type='RSI',
             mom_period=7,
             cooldown_period=7,
             thresholds=(0.25, 0.75),
             amount=5000,
             fees=20,
             ledger='ledger_mom.txt'):
    '''
    Algorithmic trading strategy based on the use of oscillators.

    Input:
        stock_prices (ndarray): the stock price data
        osc_type (str, default RSI): oscillator to use (RSI or stochastic)
        mom_period (int, default 7): number of days used to calculate oscillator
        cooldown_period (int, default 7): number of days to wait between actions
        thresholds (tuple (len2), deafault (0.25, 0.75)): thresholds used to determine buying and selling days
        amount (float, default 5000): how much we spend on each purchase (must cover fees)
        fees (float, default 20): transaction fees
        ledger (str): path to the ledger file

    Output: 
        None
    '''
    # Number of stocks simulated
    N = int(stock_prices.shape[1])
    # Create day 0 portfolio
    portfolio = proc.create_portfolio(np.repeat(amount, N), stock_prices, fees,
                                      ledger)

    for stock_id in range(N):
        # Get oscillator values
        oscillator = indi.oscillator(stock_prices[:, stock_id],
                                     n=mom_period,
                                     osc_type=osc_type)
        # Set NaNs to 0
        nans_locs = np.isnan(oscillator)
        oscillator[nans_locs] = 0
        # Find buy days and sell days
        buy_days = np.where(oscillator < thresholds[0])[0]
        sell_days = np.where(oscillator > thresholds[1])[0]

        # Oscillator values are only valid when day >= mom_period
        day = mom_period

        # Perform transactions with cooldown
        while day < len(stock_prices[:, stock_id]):
            if day in buy_days:
                proc.buy(day, stock_id, amount, stock_prices, fees, portfolio,
                         ledger)
                day += cooldown_period
            elif day in sell_days:
                proc.sell(day, stock_id, stock_prices, fees, portfolio, ledger)
                day += cooldown_period
            else:
                day += 1
        # Sell all stock on final day
        sell_all_stock(stock_prices, fees, portfolio, ledger)
Beispiel #4
0
def momentum(stock_prices,available_capital,fees,portfolio,n=7,osc_type='stochastic',ledger = 'ledger_momentum.txt'):
    '''
        OSC decides whether to buy or sell

        Input:
            stock_prices(array): All prices of all stocks
            available_capital(float): Budget for each stock purchased each time
            fees(float): Cost per transaction
            fees(float): Cost per transaction
            portfolio
            ledger(str): File to store purchase information
        Output: None
        '''
    for stocks in range(len(stock_prices[0])):
        # Calculate OSC
        result = indi.oscillator(stock_prices[:,stocks], n=n, osc_type='stochastic').tolist()
        # Initiate the event list of purchase time and sales
        buy_point = []
        sell_point = []
        for i in range(0, len(result)):
            # Judging whether to buy or sell by threshold, the cooling off period for each transaction is 10 days
            if result[i] > 0.2 and result[i] < 0.3:
                if len(buy_point) > 0 and len(sell_point) > 0:
                    if i > max(buy_point[-1],sell_point[-1]) + 5:
                        buy_point.append(i)
                else:
                    buy_point.append(i)
            elif result[i] > 0.7 and result[i] < 0.8:
                if len(buy_point) > 0 and len(sell_point) > 0:
                    if i > max(buy_point[-1],sell_point[-1]) + 5:
                        sell_point.append(i)
                else:
                    sell_point.append(i)
        # Buy and sell stocks according to the time just set
        for day in range(len(stock_prices)):
            if day+1 in buy_point:
                proc.buy(day,stocks,available_capital,stock_prices,fees,portfolio,ledger)
            elif day+1 in sell_point:
                proc.sell(day,stocks,stock_prices,fees,portfolio,ledger)
def momentum(stock_prices, osc_type = 'stochastic', lower = 0.25, upper = 0.75, n = 7, wait_time = 3, plot = False, smoothing_period = False, amount = 5000, fees = 20, ledger = 'momentum_ledger.txt'):
    '''
    Decide to sell shares in a portfolio when chosen oscillator is above upper threshold and buy when below lower threshold.
    Only buys/sells after wait_time (days) and only buys/sells once every time threshold is crossed.
    
    Input:
        stock_prices (ndarray): the stock price data
        osc_type (str, default 'stochastic'): either 'stochastic' or 'RSI' to choose an oscillator.
        lower (float, default 0.25): lower threshold
        upper (float, default 0.75): upper threshold
        n (int, default 7): period of the oscillator (in days).
        wait_time (int, default 10): period (in days) to wait before buying/selling stock if price remains below/above threshold.               
        plot (boolean, default False): shows plot of oscillator if True.
        smoothing_period (int, default = False): period of moving average to be applied to the oscillator.
        amount (float, default 5000): how much we spend on each purchase
            (must cover fees)
        
        fees (float, default 20): transaction fees
        ledger (str, default 'crossing_average_ledger.txt'): path to the ledger file
        
    Output: None
    
    Example:
        Perform momentum strategy with 7-day RSI with thresholds 0.2 and 0.8, 4-day wait time and no smoothing period on a given portfolio.
        Returns None
        >>> momentum(stock_price_data, osc_type = 'RSI', lower = 0.2, upper = 0.8, wait_time = 4)
    '''
    
    # get number of stocks and number of days
    total_days, N = stock_prices.shape
    
    # initialize portfolio
    portfolio = proc.create_portfolio(N * [amount], stock_prices, fees, ledger)
    
    # initialize oscillator array
    oscillator = np.zeros(stock_prices.shape)
    
    # get the oscillator for each stock
    oscillator = ind.oscillator(stock_prices, n, osc_type, smoothing_period)
    
    # get starting day of trading
    if smoothing_period != False and smoothing_period != 0:        
        day_1 = n + smoothing_period - 1
    
    else:
        day_1 = n - 1
    
    if wait_time > 0:
        # initialize indicator matrix which indicates we can buy after waiting time
        indicator_matrix = np.zeros(oscillator.shape)

        # now loop through each day to decide whether to buy or sell and implement the wait time
        for day in range(day_1, total_days):

           # check if oscillator is below lower threshold and this is first time we cross threshold
            stocks_to_buy_later = np.where((oscillator[day] < lower) & (oscillator[(day - 1)] >= lower))[0]
            # indicate to buy later
            indicator_matrix[day, stocks_to_buy_later] = 1

            # check indicator matrix, if 1 and stock remained below threshold we buy
            stocks_to_buy_now = np.where((indicator_matrix[(day - wait_time)] == 1) & (np.all(oscillator[(day - wait_time) : (day + 1)] < lower, axis = 0)))[0]
            # if there are stocks to buy, buy them
            if np.any(stocks_to_buy_now):
                for stock in stocks_to_buy_now:
                    proc.buy(day, stock, amount, stock_prices, fees, portfolio, ledger)

           # check if oscillator is above upper threshold and this is first time we cross threshold
            stocks_to_sell_later = np.where((oscillator[day] > upper) & (oscillator[(day - 1)] <= upper))[0]
            # indicate to sell later
            indicator_matrix[day, stocks_to_sell_later] = 1

            # check indicator matrix, if 1 and stock remained below threshold we buy
            stocks_to_sell_now = np.where((indicator_matrix[(day - wait_time)] == 1) & (np.all(oscillator[(day - wait_time) : (day + 1)] > upper, axis = 0)))[0]
            # if there are stocks to sell, sell them
            if np.any(stocks_to_sell_now) != 0:
                for stock in stocks_to_sell_now:
                    proc.sell(day, stock, stock_prices, fees, portfolio, ledger)          

    else:
        # now loop through each day to decide whether to buy or sell and implement the wait time
        for day in range(day_1, total_days):

           # check if oscillator is below lower threshold and this is first time we cross threshold
            stocks_to_buy_now = np.where((oscillator[day] < lower) & (oscillator[(day - 1)] >= lower ))[0]
           
            # if there are stocks to buy, buy them
            if np.any(stocks_to_buy_now):
                for stock in stocks_to_buy_now:
                    proc.buy(day, stock, amount, stock_prices, fees, portfolio, ledger)

           # check if oscillator is above upper threshold and this is first time we cross threshold
            stocks_to_sell_now = np.where((oscillator[day] > upper) & (oscillator[(day - 1)] <= upper))[0]
            
            # if there are stocks to sell, sell them
            if np.any(stocks_to_sell_now) != 0:
                for stock in stocks_to_sell_now:
                    proc.sell(day, stock, stock_prices, fees, portfolio, ledger)                              
    # sell portfolio at the end
    for stock_number in range(N):
        proc.sell(total_days - 1, stock_number, stock_prices, fees, portfolio, ledger)
    
    # option to plot oscillator with thresholds
    if plot == True:
        for i in range(N):
            plt.plot(oscillator[:, i], label = f'Stock {i}')
        plt.hlines(upper, n, total_days, colors = 'r', linestyles = '--', label = 'Upper')
        plt.hlines(lower, n, total_days, colors = 'r', linestyles = '--', label = 'Lower')
        plt.xlabel('Time (days)')
        plt.ylabel('Value')
        plt.legend()
        plt.title(f'{osc_type} Oscillator, Upper Threshold = {upper}, Lower Threshold = {lower}')
        plt.grid()
def momentum(stock_prices,
             threshold_high,
             threshold_low,
             amount=5000,
             fees=20,
             n=7,
             m=5,
             cool_period=7,
             osc_type='stochastic',
             ledger='ledger_momentum.txt'):
    '''

    Input:
        stock_prices (ndarray): the stock price data
        period (int, default 7): how often we buy/sell (days)
        amount (float, default 5000): how much we spend on each purchase
            (must cover fees)
        fees (float, default 20): transaction fees
        ledger (str): path to the ledger file

    Output: None
    
    general thought: buy only if it's stayed below the threshold for a number of consecutive m days,
                     sell only if it's stayed above the threshold for a number of consecutive m days.
    '''
    num_stock = stock_prices.shape[1]
    days = stock_prices.shape[0]

    #create portfolio at day 0
    portfolio = proc.create_portfolio([amount] * num_stock,
                                      stock_prices,
                                      fees,
                                      ledger=ledger)
    for i in range(num_stock):
        # to create a osc array with length of days, we add a array of all elements equal 0.5 to original osc.As 0.5<0.7
        # and 0.5>0.3
        osc = np.hstack(([0.5] * (n - 1),
                         ind.oscillator(stock_price=stock_prices[:, i],
                                        n=n,
                                        osc_type=osc_type)))
        flag = 0  # count for cool_down period
        for j in range(days - m + 2):  # check from day 0 to day days-m+1
            if flag > 0:
                flag -= 1
                continue
            if np.all(np.isnan(osc[j:j +
                                   m]) == False) == True:  # there is no nan
                # if from day j to day j+m-1, all osc> threshold_high, then sell the stock at day j+m-1
                if np.all(osc[j:j + m] > threshold_high) == True:
                    proc.sell(j + m - 1, i, stock_prices, fees, portfolio,
                              ledger)
                    flag = cool_period
                # if from day j to day j+m-1, all osc< threshold_low, then buy the stock at day j+m-1
                elif np.all(osc[j:j + m] < threshold_low) == True:  #buy
                    proc.buy(j + m - 1, i, amount, stock_prices, fees,
                             portfolio, ledger)
                    flag = cool_period
                else:
                    portfolio = portfolio
            else:  # sell at priice 0
                proc.sell(j, i, np.zeros((days, num_stock)), fees, portfolio,
                          ledger)
                break
    for k in range(num_stock):
        if portfolio[k] != 0:
            proc.sell(days - 1, k, stock_prices, fees, portfolio, ledger)
    return None
def momentum(stock_prices,
             period=7,
             low_threshold=0.3,
             up_threshold=0.7,
             amount=5000,
             fees=20,
             osc_type="stochastic",
             ledger='ledger_random.txt',
             cool_down_period=4):
    """
    using oscillator indicator to get portfolio.
    Input:
        stock_prices(ndarray): stock prices
        period: n of FMA
        low_threshold: the down threshold of indicator
        up_threshold: the up threshold of indicator
        amount: input amount
        osc_type: oscillator type
        fees: fees of transaction
        ledger: ledger file name
        cool_down_period: time to cool down
    Output:
        osc: osc result
    """
    osc = np.full_like(stock_prices, None)
    cool_down_table = [0] * stock_prices.shape[1]
    for stock_no in range(stock_prices.shape[1]):
        osc[period - 1:,
            stock_no] = indic.oscillator(stock_prices[:, stock_no], period,
                                         osc_type)
    portfolio = proc.create_portfolio([amount] * stock_prices.shape[1],
                                      stock_prices, fees, ledger)
    for i in range(1, len(stock_prices)):
        for stock_no, share in enumerate(portfolio):
            if cool_down_table[stock_no] > 0:
                cool_down_table[stock_no] -= 1
                continue
            # if share is -1, it means the company is bankruptcy
            if share == -1:
                continue
            elif stock_prices[i, stock_no] is None or np.isnan(
                    stock_prices[i, stock_no]):
                # It defines that when element in portfolio is -1, it means never buy this company's stock.
                portfolio[stock_no] = -1
                continue
            else:
                if osc[i, stock_no] > up_threshold:
                    proc.buy(i, stock_no, amount, stock_prices, fees,
                             portfolio, ledger)
                elif osc[i, stock_no] < low_threshold:
                    proc.sell(i, stock_no, stock_prices, fees, portfolio,
                              ledger)
                cool_down_table[stock_no] = cool_down_period
    last_day = len(stock_prices) - 1
    for stock_no, share in enumerate(portfolio):
        if share > 0 and stock_prices[last_day, stock_no] is not None:
            proc.sell(last_day,
                      stock_no,
                      stock_prices,
                      fees,
                      portfolio,
                      ledger_file=ledger)
    return osc