def MACD(df_quotes, fast=12, slow=26, signal=9, symbol=None): if symbol: outpath = INDICATORS_OUTPUT_PATH / '{}/{}_MACD_{}_{}_{}.pkl'.format( symbol, quotes_range(df_quotes), fast, slow, signal) if os.path.exists(outpath): return pd.read_pickle(outpath) df = pd.DataFrame(index=df_quotes.index) fast_ema = EMA(df_quotes, fast, symbol=symbol) slow_ema = EMA(df_quotes, slow, symbol=symbol) df['MACD'] = fast_ema.EMA - slow_ema.EMA signal_ema = EMA(df, signal, field='MACD', symbol=symbol) df['Signal'] = signal_ema.EMA df['MACDCrossoverSignal'] = np.where( np.logical_and(df.MACD > df.Signal, df.MACD.shift(1) <= df.Signal.shift(1)), 1, 0) df['SignalCrossoverMACD'] = np.where( np.logical_and(df.MACD < df.Signal, df.Signal.shift(1) <= df.MACD.shift(1)), 1, 0) df = utils.round_df(df) if symbol: if not os.path.exists(outpath.parent): os.makedirs(outpath.parent) df.to_pickle(outpath) return df
def run(self, symbol, df_quotes, df_indicator=None): if self.is_updated(df_quotes, df_indicator): return df_indicator df = pd.DataFrame(index=df_quotes.index) ema = self.factory.create(EMA, period=self.period, field='Volume').run(symbol, df_quotes) df[self.Columns.VOLUME.value] = df_quotes.Volume df[self.Columns.EMA.value] = ema.EMA df[self.Columns.UP.value] = np.where(df_quotes.Open < df_quotes.Close, df_quotes.Volume, 0) df[self.Columns.DOWN.value] = np.where( df_quotes.Open >= df_quotes.Close, df_quotes.Volume, 0) self.add_direction( df, np.logical_and( df[self.Columns.VOLUME.value] > df[self.Columns.EMA.value], df[self.Columns.VOLUME.value].shift(1) < df[self.Columns.EMA.value].shift(1)), np.logical_and( df[self.Columns.VOLUME.value] < df[self.Columns.EMA.value], df[self.Columns.VOLUME.value].shift(1) > df[self.Columns.EMA.value].shift(1))) df = utils.round_df(df) return df
def run(self, symbol, df_quotes, df_indicator=None): if self.is_updated(df_quotes, df_indicator): return df_indicator df = pd.DataFrame(index=df_quotes.index) for col in self.columns: df['{}{}'.format(self.SMA_COLUMN, col)] = self.factory.create(SMA, period=col).run( symbol, df_quotes)[self.SMA_COLUMN] col_size = len(self.columns) df_comparison = df.lt(df_quotes.Close, axis=0) df_comparison['CountSMABelowPrice'] = round( 100 * (df_comparison.filter(like=self.SMA_COLUMN) == True).astype(int).sum(axis=1) / col_size) df_comparison['CountSMAAbovePrice'] = round( 100 * -(df_comparison.filter(like=self.SMA_COLUMN) == False).astype(int).sum(axis=1) / col_size) df[self.Columns.TREND_STRENGTH. value] = df_comparison.CountSMABelowPrice + df_comparison.CountSMAAbovePrice self.add_direction( df, np.logical_and( df[self.Columns.TREND_STRENGTH.value] >= 100, df[self.Columns.TREND_STRENGTH.value].shift(1) < 100), np.logical_and( df[self.Columns.TREND_STRENGTH.value] <= -100, df_quotes.High < df.filter(like=self.SMA_COLUMN).min(axis=1))) df = utils.round_df(df) return df
def EMA(df_quotes, period, field='Close', symbol=None): if symbol: outpath = INDICATORS_OUTPUT_PATH / '{}/{}_{}_EMA_{}.pkl'.format( symbol, quotes_range(df_quotes), field, period) if os.path.exists(outpath): return pd.read_pickle(outpath) c = 2. / (period + 1.) df = pd.DataFrame(columns=['EMA'], index=df_quotes.index) sma = SMA(df_quotes, period, field) _sma = sma.dropna() if len(_sma.index.values) == 0: print('ts') df.loc[_sma.index.values[0], 'EMA'] = _sma.SMA.values[0] for i in range(1, len(df_quotes)): prev_ema = df.iloc[i - 1] if pd.isnull(prev_ema.EMA): continue price = df_quotes.iloc[i] ema_value = c * price[field] + (1. - c) * prev_ema.EMA df.loc[df_quotes.index.values[i], 'EMA'] = ema_value df = utils.round_df(df) if symbol: if not os.path.exists(outpath.parent): os.makedirs(outpath.parent) df.to_pickle(outpath) return df
def run(self, symbol, df_quotes, df_indicator=None): if self.is_updated(df_quotes, df_indicator): return df_indicator df = pd.DataFrame(index=df_quotes.index) fast_sma = self.factory.create(SMA, period=self.fast).run( symbol, df_quotes) slow_sma = self.factory.create(SMA, period=self.slow).run( symbol, df_quotes) df[self.Columns.FAST.value] = fast_sma.SMA df[self.Columns.SLOW.value] = slow_sma.SMA df[self.Columns.SLOW_ON_TOP.value] = np.where( np.logical_and( df[self.Columns.FAST.value] <= df[self.Columns.SLOW.value], df[self.Columns.FAST.value].shift(1) > df[self.Columns.SLOW.value].shift(1)), 1, 0) df[self.Columns.FAST_ON_TOP.value] = np.where( np.logical_and( df[self.Columns.FAST.value] >= df[self.Columns.SLOW.value], df[self.Columns.SLOW.value].shift(1) > df[self.Columns.FAST.value].shift(1)), 1, 0) self.add_direction( df, df[self.Columns.FAST.value] > df[self.Columns.SLOW.value], df[self.Columns.SLOW.value] > df[self.Columns.FAST.value]) df = utils.round_df(df) return df
def SMA_cross(df_quotes, fast=40, slow=60, symbol=None, field='Close'): if symbol: outpath = INDICATORS_OUTPUT_PATH / '{}/{}_{}_SMA_cross_{}_{}.pkl'.format( symbol, quotes_range(df_quotes), field, fast, slow) if os.path.exists(outpath): return pd.read_pickle(outpath) df = pd.DataFrame(index=df_quotes.index) fast_sma = SMA(df_quotes, fast, field=field, symbol=symbol) slow_sma = SMA(df_quotes, slow, field=field, symbol=symbol) df['FastSMA'] = fast_sma.SMA df['SlowSMA'] = slow_sma.SMA df['SlowCrossoverFast'] = np.where( np.logical_and(df.FastSMA <= df.SlowSMA, df.FastSMA.shift(1) > df.SlowSMA.shift(1)), 1, 0) df['FastCrossoverSlow'] = np.where( np.logical_and(df.FastSMA >= df.SlowSMA, df.SlowSMA.shift(1) > df.FastSMA.shift(1)), 1, 0) df = utils.round_df(df) if symbol: if not os.path.exists(outpath.parent): os.makedirs(outpath.parent) df.to_pickle(outpath) return df
def run(self, symbol, df_quotes, df_indicator=None): if self.is_updated(df_quotes, df_indicator): return df_indicator df = pd.DataFrame(columns=[_.value for _ in self.Columns], index=df_quotes.index) df[self.Columns.HIGH.value] = df_quotes.High.rolling( window=self.high).max() df[self.Columns.LOW.value] = df_quotes.Low.rolling( window=self.low).min() df[self.Columns.MID.value] = (df[self.Columns.HIGH.value] + df[self.Columns.LOW.value]) / 2 self.add_direction( df, np.logical_and( df[self.Columns.HIGH.value].shift(1) < df[self.Columns.HIGH.value], df[self.Columns.LOW.value].shift(1) <= df[self.Columns.LOW.value]), np.logical_or( df[self.Columns.LOW.value].shift(1) > df[self.Columns.LOW.value], df[self.Columns.HIGH.value].shift(1) > df[self.Columns.HIGH.value])) df = utils.round_df(df) return df
def SLSMA(df_quotes, s_fast=40, s_slow=60, l_fast=100, l_slow=150, field='Close', symbol=None): if symbol: outpath = INDICATORS_OUTPUT_PATH / '{}/{}_{}_SLSMA_cross_{}_{}.pkl'.format( symbol, quotes_range(df_quotes), field, s_fast, s_slow, l_fast, l_slow) if os.path.exists(outpath): return pd.read_pickle(outpath) # For charting... df = pd.DataFrame(index=df_quotes.index) s_fast_sma = SMA(df_quotes, s_fast, field=field, symbol=symbol) s_slow_sma = SMA(df_quotes, s_slow, field=field, symbol=symbol) l_fast_sma = SMA(df_quotes, l_fast, field=field, symbol=symbol) l_slow_sma = SMA(df_quotes, l_slow, field=field, symbol=symbol) df['S_FastSMA'] = s_fast_sma.SMA df['S_SlowSMA'] = s_slow_sma.SMA df['L_FastSMA'] = l_fast_sma.SMA df['L_SlowSMA'] = l_slow_sma.SMA df = utils.round_df(df) if symbol: if not os.path.exists(outpath.parent): os.makedirs(outpath.parent) df.to_pickle(outpath) return df
def volume(df_quotes, period=20): df = pd.DataFrame(index=df_quotes.index) ema = EMA(df_quotes, period=period, field='Volume') df['Volume'] = df_quotes.Volume df['EMA'] = ema.EMA df = utils.round_df(df) return df
def ATR(df_quotes, period=10, symbol=None): if symbol: outpath = INDICATORS_OUTPUT_PATH / '{}/{}_ATR_{}.pkl'.format( symbol, quotes_range(df_quotes), period) if os.path.exists(outpath): return pd.read_pickle(outpath) df = pd.DataFrame(columns=['ATR'], index=df_quotes.index) df_true_range = true_range(df_quotes) for i in range(1 + len(df_quotes) - period): if pd.isnull(df_true_range.iloc[i].true_range): continue start = i end = i + period last_index = end - 1 trs = df_true_range[start:end] prev_atr = df.iloc[last_index - 1].ATR if pd.isnull(prev_atr): atr = np.mean([tr for tr in trs.true_range.values]) else: atr = (prev_atr * (period - 1) + df_true_range.iloc[last_index].true_range) / period df.loc[df_quotes.index.values[last_index], 'ATR'] = atr if symbol: if not os.path.exists(outpath.parent): os.makedirs(outpath.parent) df.to_pickle(outpath) return utils.round_df(df)
def trend_strength_indicator(df_quotes, start=40, end=150, step=5, symbol=None): if symbol: outpath = INDICATORS_OUTPUT_PATH / '{}/{}_trend_strength_indicator_{}_{}_{}.pkl'.format( symbol, quotes_range(df_quotes), start, end, step) if os.path.exists(outpath): return pd.read_pickle(outpath) df = pd.DataFrame(index=df_quotes.index) columns = [x for x in range(start, end, step)] columns += [end] for col in columns: df['SMA{}'.format(col)] = SMA(df_quotes, col, symbol=symbol) col_size = len(columns) df_comparison = df.lt(df_quotes.Close, axis=0) df_comparison['CountSMABelowPrice'] = round( 100 * (df_comparison.filter(like='SMA') == True).astype(int).sum(axis=1) / col_size) df_comparison['CountSMAAbovePrice'] = round( 100 * -(df_comparison.filter(like='SMA') == False).astype(int).sum(axis=1) / col_size) df['TrendStrength'] = df_comparison.CountSMABelowPrice + df_comparison.CountSMAAbovePrice df = utils.round_df(df) if symbol: if not os.path.exists(outpath.parent): os.makedirs(outpath.parent) df.to_pickle(outpath) return df
def true_range(self, df_quotes): df = pd.DataFrame(index=df_quotes.index) df['H_minus_L'] = df_quotes.High - df_quotes.Low df['H_minus_PrevC'] = abs(df_quotes.High - df_quotes.shift(1).Close) df['L_minus_PrevC'] = abs(df_quotes.Low - df_quotes.shift(1).Close) df = utils.round_df(df, 4) df['true_range'] = df.max(axis=1) return df.filter(like='true_range')
def run(self, symbol, df_quotes, df_indicator=None): if self.is_updated(df_quotes, df_indicator): return df_indicator df = pd.DataFrame(index=df_quotes.index) df['STDEV'] = df_quotes[self.field].rolling(self.period).std() self.add_direction(df, df_quotes[self.field] > df['STDEV'], df_quotes[self.field] < df['STDEV']) df = utils.round_df(df) return df
def trailing_stops(df_quotes, multiplier=4, period=10, symbol=None): if symbol: outpath = INDICATORS_OUTPUT_PATH / '{}/{}_trailing_stops_{}_{}.pkl'.format( symbol, quotes_range(df_quotes), period, multiplier) if os.path.exists(outpath): return pd.read_pickle(outpath) df = pd.DataFrame(columns=['BuyStops', 'SellStops'], index=df_quotes.index) df_atr = ATR(df_quotes, period=period, symbol=symbol) sign = -1 # SellStops: -1, BuyStops: 1 for i in range(len(df_quotes) - 1): if pd.isnull(df_atr.iloc[i].ATR): continue start = i - period end = i quotes = df_quotes.iloc[start + 1:end + 1] cur_quote = df_quotes.iloc[i] next_quote = df_quotes.iloc[i + 1] _atr = df_atr.iloc[i].ATR # close_price = next_quote.Close # trend_dir_sign = -1 if close_price > _atr else 1 max_price = quotes.Close.max() min_price = quotes.Close.min() sell = max_price + sign * (multiplier * _atr) buy = min_price + sign * (multiplier * _atr) sell = [sell, df.iloc[i].SellStops] buy = [buy, df.iloc[i].BuyStops] try: sell = np.max([x for x in sell if not pd.isnull(x)]) buy = np.min([x for x in buy if not pd.isnull(x)]) except: print(sell) if sign < 0: df.set_value(index=df_quotes.index.values[i + 1], col='SellStops', value=sell) if next_quote.Close <= sell: sign = 1 else: df.set_value(index=df_quotes.index.values[i + 1], col='BuyStops', value=buy) if next_quote.Close >= buy: sign = -1 df = utils.round_df(df) if symbol: if not os.path.exists(outpath.parent): os.makedirs(outpath.parent) df.to_pickle(outpath) return df
def generate_equity_curve(df_trades, starting_balance, historical_data, selling_fees_method=None, start_date=None, end_date=None): df_trades['StartDate'] = pd.to_datetime(df_trades['StartDate']) df_trades['EndDate'] = pd.to_datetime(df_trades['EndDate']) df_trades['LastRecordDate'] = pd.to_datetime(df_trades['LastRecordDate']) if start_date is None: start_date = df_trades.StartDate.min() if end_date is None: end_date = df_trades.LastRecordDate.max() start_date = pd.to_datetime(start_date) df_quotes = historical_data.copy() if start_date: df_quotes = df_quotes.loc[start_date:] if end_date: df_quotes = df_quotes.loc[:end_date] df = pd.DataFrame() for index in df_quotes.index.values.astype('datetime64[D]'): date = pd.to_datetime(index) cash = starting_balance - df_trades.loc[df_trades['StartDate'] <= date].BuyValue.sum() cash_value = cash + df_trades.loc[df_trades['EndDate'] <= date].SellValue.dropna().sum() current_value = 0 open_trades = df_trades.loc[df_trades['StartDate'] <= date] open_trades = open_trades[(open_trades['EndDate'] > date) | (pd.isnull(open_trades['EndDate']))] for open_trade_index in open_trades.index: open_trade = open_trades.loc[open_trade_index] prices = df_quotes.loc[:date]['{}_Close'.format(open_trade.Symbol)].dropna().values price = prices[-1] if len(prices) > 0 else open_trade['LastPrice'] shares = open_trade.Shares value = price * shares if selling_fees_method is not None: value = price * shares - selling_fees_method(price, shares) current_value += value equity_value = cash_value + current_value index = pd.to_datetime(index) df.loc[index, 'Cash'] = cash_value df.loc[index, 'Equity'] = equity_value exposure_pct = 100 * current_value / equity_value df.loc[index, 'Exposure %'] = exposure_pct if not df.empty: before_start_date = pd.to_datetime(df.index.values[0]) - datetime.timedelta(days=1) df.loc[before_start_date, 'Cash'] = starting_balance df.loc[before_start_date, 'Equity'] = starting_balance df.loc[before_start_date, 'Exposure %'] = 0.0 df = df.sort_index() df['Drawdown'] = df['Equity'].expanding().apply(drawdown) df['DrawdownPercent'] = df['Equity'].expanding().apply(drawdown_pct) df = utils.round_df(df) return df
def run(self, symbol, df_quotes, df_indicator=None): if self.is_updated(df_quotes, df_indicator): return df_indicator else: df = pd.DataFrame(index=df_quotes.index) df[self.Columns.SMA.value] = df_quotes[self.field].rolling( self.period).mean() self.add_direction( df, df_quotes[self.field] > df[self.Columns.SMA.value], df_quotes[self.field] < df[self.Columns.SMA.value]) df = utils.round_df(df) return df
def run(self, symbol, df_quotes, df_indicator=None): if self.is_updated(df_quotes, df_indicator): return df_indicator df = pd.DataFrame(columns=['ATR'], index=df_quotes.index) df_true_range = self.true_range(df_quotes) df['ATR'] = df_true_range.true_range.rolling(self.period).mean() for i in df.index.values: if pd.isnull(df['ATR'].shift(1).loc[i]): continue df.loc[i, 'ATR'] = (df['ATR'].shift(1).loc[i] * (self.period - 1) + df_true_range.loc[i].true_range) / self.period self.add_direction(df, False, False) return utils.round_df(df)
def run(self, symbol, df_quotes, df_indicator=None): if self.is_updated(df_quotes, df_indicator): return df_indicator df = pd.DataFrame(columns=['BuyStops', 'SellStops'], index=df_quotes.index) df_atr = self.factory.create(ATR, period=self.period).run( symbol, df_quotes) sign = -1 # SellStops: -1, BuyStops: 1 for i in range(len(df_quotes) - 1): if pd.isnull(df_atr.iloc[i].ATR): continue start = i - self.period end = i quotes = df_quotes.iloc[start + 1:end + 1] cur_quote = df_quotes.iloc[i] next_quote = df_quotes.iloc[i + 1] _atr = df_atr.iloc[i].ATR # close_price = next_quote.Close # trend_dir_sign = -1 if close_price > _atr else 1 max_price = quotes.Close.max() min_price = quotes.Close.min() sell = max_price + sign * (self.multiplier * _atr) buy = min_price + sign * (self.multiplier * _atr) sell = [sell, df.iloc[i].SellStops] buy = [buy, df.iloc[i].BuyStops] try: sell = np.max([x for x in sell if not pd.isnull(x)]) buy = np.min([x for x in buy if not pd.isnull(x)]) except: print(sell) if sign < 0: df.loc[df_quotes.index.values[i + 1]]['SellStops'] = sell if next_quote.Close <= sell: sign = 1 else: df.loc[df_quotes.index.values[i + 1]]['BuyStops'] = buy if next_quote.Close >= buy: sign = -1 self.add_direction(df, df_quotes.Close >= df.BuyStops, df_quotes.Close <= df.SellStops) df = utils.round_df(df) return df
def STDEV(df_quotes, period, field='Close', symbol=None): if symbol: outpath = INDICATORS_OUTPUT_PATH / '{}/{}_{}_STDEV_{}.pkl'.format( symbol, quotes_range(df_quotes), field, period) if os.path.exists(outpath): return pd.read_pickle(outpath) df = pd.DataFrame(index=df_quotes.index) df['STDEV'] = df_quotes[field].rolling(period).std() df = utils.round_df(df) if symbol: if not os.path.exists(outpath.parent): os.makedirs(outpath.parent) df.to_pickle(outpath) return df
def run(self, symbol, df_quotes, df_indicator=None): if self.is_updated(df_quotes, df_indicator): return df_indicator df = pd.DataFrame(index=df_quotes.index) df_sma = self.factory.create(SMA, period=self.period).run( symbol, df_quotes) df_stdev = self.factory.create(STDEV, period=self.period).run( symbol, df_quotes) df['Top'] = df_sma.SMA + (df_stdev.STDEV * self.stdev) df['Mid'] = df_sma.SMA df['Bottom'] = df_sma.SMA - (df_stdev.STDEV * self.stdev) self.add_direction(df, df_quotes.Close >= df.Top, df_quotes.High < df.Bottom) df = utils.round_df(df) return df
def donchian_channel(df_quotes, high=50, low=50, symbol=None): if symbol: outpath = INDICATORS_OUTPUT_PATH / '{}/{}_donchian_channel_{}_{}.pkl'.format( symbol, quotes_range(df_quotes), high, low) if os.path.exists(outpath): return pd.read_pickle(outpath) df = pd.DataFrame(columns=['high', 'mid', 'low'], index=df_quotes.index) df['high'] = df_quotes.High.rolling(window=high).max() df['low'] = df_quotes.Low.rolling(window=low).min() df['mid'] = (df.high + df.low) / 2 df = utils.round_df(df) if symbol: if not os.path.exists(outpath.parent): os.makedirs(outpath.parent) df.to_pickle(outpath) return df
def update(self, date, account: Account): if len(self.df.index.values) == 0: earlier = pd.to_datetime(date) - datetime.timedelta(days=1) self.df.loc[earlier] = pd.Series() self.df.loc[earlier, EquityCurveKey.EQUITY.value] = account.starting_balance self.df.loc[earlier, EquityCurveKey.CASH.value] = account.starting_balance self.df.loc[date] = pd.Series() self.df.loc[date, EquityCurveKey.EQUITY.value] = account.equity self.df.loc[date, EquityCurveKey.CASH.value] = account.cash self.df[EquityCurveKey.DRAWDOWN.value] = self.df[ EquityCurveKey.EQUITY.value].expanding(1).apply( lambda d: -(d.max() - d[-1])) self.df[EquityCurveKey.DRAWDOWN_PERCENT.value] = self.df[ EquityCurveKey.EQUITY.value].expanding(1).apply( lambda d: -(100 * (d.max() - d[-1]) / d.max())) self.df = utils.round_df(self.df)
def bollinger_band(df_quotes, period=60, stdev=1.2, symbol=None): if symbol: outpath = INDICATORS_OUTPUT_PATH / '{}/{}_bollinger_band_{}_{}.pkl'.format( symbol, quotes_range(df_quotes), period, stdev) if os.path.exists(outpath): return pd.read_pickle(outpath) df = pd.DataFrame(index=df_quotes.index) df_sma = SMA(df_quotes, period, symbol=symbol) df_stdev = STDEV(df_quotes, period, symbol=symbol) df['UP'] = df_sma.SMA + (df_stdev.STDEV * stdev) df['MID'] = df_sma.SMA df['LOW'] = df_sma.SMA - (df_stdev.STDEV * stdev) df = utils.round_df(df) if symbol: if not os.path.exists(outpath.parent): os.makedirs(outpath.parent) df.to_pickle(outpath) return df
def run(self, symbol, df_quotes, df_indicator=None): if self.is_updated(df_quotes, df_indicator): return df_indicator df_top_atr = self.factory.create(ATR, period=self.top).run( symbol, df_quotes) df_bottom_atr = self.factory.create(ATR, period=self.bottom).run( symbol, df_quotes) df_sma = self.factory.create(SMA, period=self.sma).run(symbol, df_quotes) df = pd.DataFrame(columns=[_.value for _ in self.Columns], index=df_quotes.index) df[self.Columns.MID.value] = df_sma.SMA df[self.Columns.TOP. value] = df[self.Columns.MID.value] + df_top_atr.ATR df[self.Columns.BOTTOM. value] = df[self.Columns.MID.value] - df_bottom_atr.ATR self.add_direction(df, df_quotes.Close > df[self.Columns.TOP.value], df_quotes.Close < df[self.Columns.BOTTOM.value]) df = utils.round_df(df) return df
def atr_channel(df_quotes, top=7, bottom=3, sma=150, symbol=None): if symbol: outpath = INDICATORS_OUTPUT_PATH / '{}/{}_atr_channel_{}_{}_{}.pkl'.format( symbol, quotes_range(df_quotes), top, bottom, sma) if os.path.exists(outpath): return pd.read_pickle(outpath) df_top_atr = ATR(df_quotes, period=top, symbol=symbol) df_bottom_atr = ATR(df_quotes, period=bottom, symbol=symbol) df_sma = SMA(df_quotes, period=sma, symbol=symbol) df = pd.DataFrame(columns=['top', 'mid', 'bottom'], index=df_quotes.index) df['mid'] = df_sma.SMA df['top'] = df.mid + df_top_atr.ATR df['bottom'] = df.mid - df_bottom_atr.ATR df = utils.round_df(df) if symbol: if not os.path.exists(outpath.parent): os.makedirs(outpath.parent) df.to_pickle(outpath) return df
def run(self, symbol, df_quotes, df_indicator=None): if self.is_updated(df_quotes, df_indicator): return df_indicator c = 2. / (self.period + 1.) df = pd.DataFrame(columns=['EMA'], index=df_quotes.index) _sma = self.factory.create(SMA, period=self.period, field=self.field).run(symbol, df_quotes).dropna() if not _sma.empty: df.loc[_sma.index.values[0], 'EMA'] = _sma.SMA.values[0] for i in range(1, len(df_quotes)): prev_ema = df.iloc[i - 1] if pd.isnull(prev_ema.EMA): continue price = df_quotes.iloc[i] ema_value = c * price[self.field] + (1. - c) * prev_ema.EMA df.loc[df_quotes.index.values[i], 'EMA'] = ema_value self.add_direction(df, df_quotes[self.field] > df['EMA'], df_quotes[self.field] < df['EMA']) df = utils.round_df(df) return df
def run(self, symbol, df_quotes, df_indicator=None): if self.is_updated(df_quotes, df_indicator): return df_indicator df = pd.DataFrame(index=df_quotes.index) fast_ema = self.factory.create(EMA, period=self.fast).run( symbol, df_quotes) slow_ema = self.factory.create(EMA, period=self.slow).run( symbol, df_quotes) df['MACD'] = fast_ema.EMA - slow_ema.EMA signal_ema = self.factory.create(EMA, period=self.signal, field='MACD').run(symbol, df) df['Signal'] = signal_ema.EMA df['MACDCrossoverSignal'] = np.where( np.logical_and(df.MACD > df.Signal, df.MACD.shift(1) <= df.Signal.shift(1)), 1, 0) df['SignalCrossoverMACD'] = np.where( np.logical_and(df.MACD < df.Signal, df.Signal.shift(1) <= df.MACD.shift(1)), 1, 0) self.add_direction(df, df['MACDCrossoverSignal'] == 1, df['SignalCrossoverMACD'] == 1) df = utils.round_df(df) return df
def translate_transactions_to_trades(self): df = self.trades.copy() close_transactions = self.transactions.loc[self.transactions['Action'] == Action.CLOSE] df['EndDate'] = close_transactions['Date'] df['Symbol'] = close_transactions['Symbol'] df['SellPrice'] = close_transactions['Price'] df['Shares'] = close_transactions['Shares'] df['SellValue'] = close_transactions['Value'] df['CloseIndicator'] = close_transactions['Indicator'] df['LastRecordDate'] = close_transactions['Date'] df['LastPrice'] = close_transactions['Price'] df['LastValue'] = close_transactions['Value'] for index in close_transactions.index.values: _df = self.transactions.copy() close_transaction = close_transactions.loc[index] open_transaction = _df.loc[ _df['Shares'] == close_transaction.Shares].loc[ _df['Date'] <= close_transaction.Date].loc[ _df['Symbol'] == close_transaction.Symbol].loc[ _df['Action'] != Action.CLOSE].iloc[-1] df_index = df.loc[df['EndDate'] == close_transaction.Date].loc[ df['Symbol'] == close_transaction.Symbol].index.values[-1] df.loc[df_index, 'StartDate'] = pd.to_datetime( open_transaction.Date).strftime('%Y-%m-%d') df.loc[df_index, 'BuyPrice'] = open_transaction.Price df.loc[df_index, 'BuyValue'] = open_transaction.Value df.loc[df_index, 'OpenIndicator'] = open_transaction.Indicator df.loc[df_index, 'TotalRisk'] = self.position_sizing.calculate_total_risk( open_transaction.Price, close_transaction.Shares) for index in self.positions.loc[ self.positions.Quantity > 0].index.values: _df = self.transactions.copy() position = self.positions.loc[index] open_transaction = _df.loc[_df['Shares'] == position.Quantity].loc[ _df['Symbol'] == position.Symbol].loc[ _df['Action'] != Action.CLOSE].iloc[-1] df = df.append(pd.DataFrame( { 'StartDate': pd.to_datetime(open_transaction.Date).strftime('%Y-%m-%d'), 'BuyPrice': open_transaction.Price, 'BuyValue': open_transaction.Value, 'OpenIndicator': open_transaction.Indicator, 'TotalRisk': self.position_sizing.calculate_total_risk( open_transaction.Price, open_transaction.Shares), 'Symbol': position.Symbol, 'Shares': open_transaction.Shares, 'LastRecordDate': position.LastRecordDate, 'LastPrice': position.LastPrice, 'LastValue': position.LastValue }, index=[0]), ignore_index=True) df = df.sort_values(['StartDate']) df = df.reset_index() df = df.drop(['index'], axis=1) try: df['BarsHeld'] = df.apply( lambda trade: len(self.df_group_quotes.loc[pd.to_datetime( trade['StartDate']):pd.to_datetime(trade[ 'LastRecordDate'])].filter(regex='^{}_Close'.format( trade.Symbol)).dropna().values), axis=1) except: pass df['PnL'] = np.subtract(df['SellValue'].values, np.absolute(df['BuyValue'].values)) df['RMultiple'] = np.divide(df['PnL'].values, df['TotalRisk']) df['LastPnL'] = np.subtract(df['LastValue'].values, np.absolute(df['BuyValue'].values)) df['LastRMultiple'] = np.divide(df['LastPnL'].values, df['TotalRisk']) df = utils.round_df(df) df['RMultiple'] = utils._round(df['RMultiple'], places=2) df['LastRMultiple'] = utils._round(df['LastRMultiple'], places=2) columns = self.trades.columns self.trades = df.copy()[columns]
def performance_data(starting_capital, df_backtest, df_trades, index='Performance'): df = pd.DataFrame() equities = df_backtest['Equity'].values years = len(equities) / TRADE_DAYS_PER_YEAR ending_capital = df_backtest['Equity'].values[-1] net_profit = ending_capital - starting_capital net_profit_pct = 100 * net_profit / starting_capital annualized_gain = ((ending_capital / starting_capital)**(1 / years) - 1) max_system_dd = max_drawdown(df_backtest) max_system_pct_dd = max_pct_drawdown(df_backtest) max_peak = df_backtest.Equity.max() df_winning_trades = df_trades[df_trades['LastPnL'] > 0] df_losing_trades = df_trades[df_trades['LastPnL'] <= 0] ui = ulcer_index(df_backtest) avg_bars_held_value = avg_bars_held(df_backtest, df_trades) avg_expectancy_pct_value = avg_expectancy_pct(df_trades) risk_free_rate = 0.01 df.loc[index, 'Number of Trading Days'] = df_backtest.Equity.count() df.loc[index, 'Starting Capital'] = starting_capital df.loc[index, 'Ending Capital'] = ending_capital df.loc[index, 'Net Profit'] = net_profit df.loc[index, 'Net Profit %'] = net_profit_pct df.loc[index, 'SQN'] = SQN(df_trades) df.loc[index, 'Annualized Gain'] = annualized_gain df.loc[index, 'Max Profit'] = df_trades.LastPnL.max() df.loc[index, 'Max Loss'] = df_trades.LastPnL.min() df.loc[index, 'Number of Trades'] = len(df_trades.index.values) df.loc[index, 'Winning Trades'] = len(df_winning_trades.index.values) df.loc[index, 'Losing Trades'] = len(df_losing_trades.index.values) try: df.loc[index, 'Winning Trades %'] = np.round( 100 * (len(df_winning_trades.index.values) / len(df_trades.index.values)), 2) except: df.loc[index, 'Winning Trades %'] = 0 df.loc[index, 'Avg Profit/Loss'] = avg_expectancy(df_trades) df.loc[index, 'Avg Profit'] = avg_expectancy(df_winning_trades) df.loc[index, 'Avg Loss'] = avg_expectancy(df_losing_trades) df.loc[index, 'Avg Profit/Loss %'] = avg_expectancy_pct_value df.loc[index, 'Avg Profit %'] = avg_expectancy_pct(df_winning_trades) df.loc[index, 'Avg Loss %'] = avg_expectancy_pct(df_losing_trades) df.loc[index, 'Avg Bars Held'] = avg_bars_held_value df.loc[index, 'Avg Winning Bars Held'] = avg_bars_held(df_backtest, df_winning_trades) df.loc[index, 'Avg Losing Bars Held'] = avg_bars_held(df_backtest, df_losing_trades) df.loc[index, 'Max System Drawdown'] = max_system_dd df.loc[index, 'Max System % Drawdown'] = max_system_pct_dd df.loc[index, 'Max Peak'] = max_peak df.loc[index, 'Recovery Factor'] = net_profit / abs(max_system_pct_dd) try: df.loc[index, 'Profit Factor'] = df_winning_trades['LastPnL'].sum() / abs( df_losing_trades['LastPnL'].sum()) except: df.loc[index, 'Profit Factor'] = 0.0 df.loc[index, 'Payoff Ratio'] = df_winning_trades['LastPnL'].mean() / abs( df_losing_trades['LastPnL'].mean()) return utils.round_df(df, places=2)