def gap(f): r"""Calculate the gap percentage between the current open and the previous close. Parameters ---------- f : pandas.DataFrame Dataframe with columns ``open`` and ``close``. Returns ------- new_column : pandas.Series (float) The array containing the new feature. References ---------- *A gap is a break between prices on a chart that occurs when the price of a stock makes a sharp move up or down with no trading occurring in between* [IP_GAP]_. .. [IP_GAP] http://www.investopedia.com/terms/g/gap.asp """ c1 = 'open' c2 = 'close[1]' vexec(f, c2) new_column = 100 * pchange2(f, c1, c2) return new_column
def diminus(f, p=14): r"""Calculate the Minus Directional Indicator (-DI). Parameters ---------- f : pandas.DataFrame Dataframe with columns ``high`` and ``low``. p : int The period over which to calculate the -DI. Returns ------- new_column : pandas.Series (float) The array containing the new feature. References ---------- *A component of the average directional index (ADX) that is used to measure the presence of a downtrend. When the -DI is sloping downward, it is a signal that the downtrend is getting stronger* [IP_NDI]_. .. [IP_NDI] http://www.investopedia.com/terms/n/negativedirectionalindicator.asp """ tr = 'truerange' vexec(f, tr) atr = USEP.join(['atr', str(p)]) vexec(f, atr) dmm = 'dmminus' f[dmm] = dminus(f) new_column = 100 * dminus(f).ewm(span=p).mean() / f[atr] return new_column
def truehigh(f): r"""Calculate the *True High* value. Parameters ---------- f : pandas.DataFrame Dataframe with columns ``high`` and ``low``. Returns ------- new_column : pandas.Series (float) The array containing the new feature. References ---------- *Today's high, or the previous close, whichever is higher* [TS_TR]_. .. [TS_TR] http://help.tradestation.com/09_01/tradestationhelp/charting_definitions/true_range.htm """ c1 = 'low[1]' vexec(f, c1) c2 = 'high' new_column = f.apply(c2max, axis=1, args=[c1, c2]) return new_column
def rsi(f, c, p=14): r"""Calculate the Relative Strength Index (RSI). Parameters ---------- f : pandas.DataFrame Dataframe containing the column ``net``. c : str Name of the column in the dataframe ``f``. p : int The period over which to calculate the RSI. Returns ------- new_column : pandas.Series (float) The array containing the new feature. References ---------- *Developed by J. Welles Wilder, the Relative Strength Index (RSI) is a momentum oscillator that measures the speed and change of price movements* [SC_RSI]_. .. [SC_RSI] http://stockcharts.com/school/doku.php?id=chart_school:technical_indicators:relative_strength_index_rsi """ cdiff = 'net' vexec(f, cdiff) f['pval'] = upc(f, cdiff) f['mval'] = dpc(f, cdiff) upcs = ma(f, 'pval', p) dpcs = ma(f, 'mval', p) new_column = 100 - (100 / (1 + (upcs / dpcs))) return new_column
def truelow(f): r"""Calculate the *True Low* value. Parameters ---------- f : pandas.DataFrame Dataframe with columns ``high`` and ``low``. Returns ------- new_column : pandas.Series (float) The array containing the new feature. References ---------- *Today's low, or the previous close, whichever is lower* [TS_TR]_. """ c1 = 'high[1]' vexec(f, c1) c2 = 'low' new_column = f.apply(c2min, axis=1, args=[c1, c2]) return new_column
def adx(f, p=14): r"""Calculate the Average Directional Index (ADX). Parameters ---------- f : pandas.DataFrame Dataframe with all columns required for calculation. If you are applying ADX through ``vapply``, then these columns are calculated automatically. p : int The period over which to calculate the ADX. Returns ------- new_column : pandas.Series (float) The array containing the new feature. References ---------- The Average Directional Movement Index (ADX) was invented by J. Welles Wilder in 1978 [WIKI_ADX]_. Its value reflects the strength of trend in any given instrument. .. [WIKI_ADX] https://en.wikipedia.org/wiki/Average_directional_movement_index """ c1 = 'diplus' vexec(f, c1) c2 = 'diminus' vexec(f, c2) # calculations dip = f[c1] dim = f[c2] didiff = abs(dip - dim) disum = dip + dim new_column = 100 * didiff.ewm(span=p).mean() / disum return new_column
def trade_system(model, system, space, intraday, name, quantity): r"""Trade the given system. Parameters ---------- model : alphapy.Model The model object with specifications. system : alphapy.System The long/short system to run. space : alphapy.Space Namespace of instrument prices. intraday : bool If True, then run an intraday system. name : str The symbol to trade. quantity : float The amount of the ``name`` to trade, e.g., number of shares Returns ------- tradelist : list List of trade entries and exits. Other Parameters ---------------- Frame.frames : dict All of the data frames containing price data. """ # Unpack the model data. directory = model.specs['directory'] extension = model.specs['extension'] separator = model.specs['separator'] # Unpack the system parameters. longentry = system.longentry shortentry = system.shortentry longexit = system.longexit shortexit = system.shortexit holdperiod = system.holdperiod scale = system.scale # Determine whether or not this is a model-driven system. entries_and_exits = [longentry, shortentry, longexit, shortexit] active_signals = [x for x in entries_and_exits if x is not None] use_model = False for signal in active_signals: if any(x in signal for x in ['phigh', 'plow']): use_model = True # Read in the price frame pf = Frame.frames[frame_name(name, space)].df # Use model output probabilities as input to the system if use_model: # get latest probabilities file probs_dir = SSEP.join([directory, 'output']) file_path = most_recent_file(probs_dir, 'probabilities*') file_name = file_path.split(SSEP)[-1].split('.')[0] # read the probabilities frame and trim the price frame probs_frame = read_frame(probs_dir, file_name, extension, separator) pf = pf[-probs_frame.shape[0]:] probs_frame.index = pf.index probs_frame.columns = ['probability'] # add probability column to price frame pf = pd.concat([pf, probs_frame], axis=1) # Evaluate the long and short events in the price frame for signal in active_signals: vexec(pf, signal) # Initialize trading state variables inlong = False inshort = False h = 0 p = 0 q = quantity tradelist = [] # Loop through prices and generate trades for dt, row in pf.iterrows(): # get closing price c = row['close'] if intraday: bar_number = row['bar_number'] end_of_day = row['end_of_day'] # evaluate entry and exit conditions lerow = row[longentry] if longentry else None serow = row[shortentry] if shortentry else None lxrow = row[longexit] if longexit else None sxrow = row[shortexit] if shortexit else None # process the long and short events if lerow: if p < 0: # short active, so exit short tradelist.append((dt, [name, Orders.sx, -p, c])) inshort = False h = 0 p = 0 if p == 0 or scale: # go long (again) tradelist.append((dt, [name, Orders.le, q, c])) inlong = True p = p + q elif serow: if p > 0: # long active, so exit long tradelist.append((dt, [name, Orders.lx, -p, c])) inlong = False h = 0 p = 0 if p == 0 or scale: # go short (again) tradelist.append((dt, [name, Orders.se, -q, c])) inshort = True p = p - q # check exit conditions if inlong and h > 0 and lxrow: # long active, so exit long tradelist.append((dt, [name, Orders.lx, -p, c])) inlong = False h = 0 p = 0 if inshort and h > 0 and sxrow: # short active, so exit short tradelist.append((dt, [name, Orders.sx, -p, c])) inshort = False h = 0 p = 0 # if a holding period was given, then check for exit if holdperiod and h >= holdperiod: if inlong: tradelist.append((dt, [name, Orders.lh, -p, c])) inlong = False if inshort: tradelist.append((dt, [name, Orders.sh, -p, c])) inshort = False h = 0 p = 0 # increment the hold counter if inlong or inshort: h += 1 if intraday and end_of_day: if inlong: # long active, so exit long tradelist.append((dt, [name, Orders.lx, -p, c])) inlong = False if inshort: # short active, so exit short tradelist.append((dt, [name, Orders.sx, -p, c])) inshort = False h = 0 p = 0 return tradelist