From f6b86242b5bdfa30f5fe30bb4a5655a41c3cee76 Mon Sep 17 00:00:00 2001 From: Mark Conway Date: Mon, 22 May 2017 12:28:09 -0400 Subject: [PATCH 001/129] replace get_yahoo_data with get_pandas_data MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Yahoo Finance has discontinued its historical quote service. For now, we’ll use the pandas-datareader to get daily quotes from Google Finance, even though that is not technically supported either. The days of free stock data appear to be coming to an end. --- alphapy/data.py | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/alphapy/data.py b/alphapy/data.py index 161b6ad..37c5a2a 100644 --- a/alphapy/data.py +++ b/alphapy/data.py @@ -369,14 +369,16 @@ def get_google_data(symbol, lookback_period, fractal): # -# Function get_yahoo_data +# Function get_pandas_data # -def get_yahoo_data(symbol, lookback_period): +def get_pandas_data(schema, symbol, lookback_period): r"""Get Yahoo Finance daily data. Parameters ---------- + schema : str + The source of the pandas-datareader data. symbol : str A valid stock symbol. lookback_period : int @@ -396,17 +398,14 @@ def get_yahoo_data(symbol, lookback_period): # Call the Pandas Web data reader. - df = web.DataReader(symbol, 'yahoo', start, end) + df = web.DataReader(symbol, schema, start, end) - # Set time series as index + # Rename columns to lower case. if len(df) > 0: - df.reset_index(level=0, inplace=True) df = df.rename(columns = lambda x: x.lower().replace(' ','')) - df['datetime'] = pd.to_datetime(df['date']) - del df['date'] - df.index = df['datetime'] - del df['datetime'] + else: + logger.info("Empty data frame for: %s", symbol) return df @@ -433,6 +432,7 @@ def get_feed_data(group, lookback_period): """ gspace = group.space + schema = gspace.schema fractal = gspace.fractal # Determine the feed source if 'd' in fractal: @@ -447,7 +447,7 @@ def get_feed_data(group, lookback_period): for item in group.members: logger.info("Getting %s data for last %d days", item, lookback_period) if daily_data: - df = get_yahoo_data(item, lookback_period) + df = get_pandas_data(schema, item, lookback_period) else: df = get_google_data(item, lookback_period, fractal) if len(df) > 0: From 53b7037d5c3538c92cce1840c370b54da439e2e9 Mon Sep 17 00:00:00 2001 From: Mark Conway Date: Mon, 22 May 2017 21:42:04 -0400 Subject: [PATCH 002/129] fix empty frame handling fix empty frame handling --- alphapy/data.py | 16 +++++++--------- alphapy/frame.py | 6 +++--- alphapy/market_variables.py | 4 ++-- 3 files changed, 12 insertions(+), 14 deletions(-) diff --git a/alphapy/data.py b/alphapy/data.py index 37c5a2a..b6e7853 100644 --- a/alphapy/data.py +++ b/alphapy/data.py @@ -398,14 +398,12 @@ def get_pandas_data(schema, symbol, lookback_period): # Call the Pandas Web data reader. - df = web.DataReader(symbol, schema, start, end) - - # Rename columns to lower case. - - if len(df) > 0: + df = None + try: + df = web.DataReader(symbol, schema, start, end) df = df.rename(columns = lambda x: x.lower().replace(' ','')) - else: - logger.info("Empty data frame for: %s", symbol) + except: + logger.info("Could not retrieve data for: %s", symbol) return df @@ -450,12 +448,12 @@ def get_feed_data(group, lookback_period): df = get_pandas_data(schema, item, lookback_period) else: df = get_google_data(item, lookback_period, fractal) - if len(df) > 0: + if df is not None and not df.empty: # allocate global Frame newf = Frame(item.lower(), gspace, df) if newf is None: logger.error("Could not allocate Frame for: %s", item) else: - logger.info("Could not get data for: %s", item) + logger.info("No DataFrame for %s", item) # Indicate whether or not data is daily return daily_data diff --git a/alphapy/frame.py b/alphapy/frame.py index 8121ef0..6723e5b 100644 --- a/alphapy/frame.py +++ b/alphapy/frame.py @@ -256,17 +256,17 @@ def load_frames(group, directory, extension, separator, splits=False): logger.info("Load Data Frame %s from file", fname) df = read_frame(directory, fname, extension, separator) # add this frame to the consolidated frame list - if len(df) > 0: + if df is not None and not df.empty: # set the name df.insert(0, 'tag', gn) all_frames.append(df) else: - logger.info("Empty Data Frame for: %s", gn) + logger.debug("Empty Data Frame for: %s", gn) else: # no splits, so use data from consolidated files fname = frame_name(gname, gspace) df = read_frame(directory, fname, extension, separator) - if df is not None: + if df is not None and not df.empty: all_frames.append(df) return all_frames diff --git a/alphapy/market_variables.py b/alphapy/market_variables.py index 1756b34..ed3b5c7 100644 --- a/alphapy/market_variables.py +++ b/alphapy/market_variables.py @@ -497,9 +497,9 @@ def vapply(group, vname, vfuncs=None): logger.debug("Applying variable %s to %s", v, g) f = vexec(f, v, vfuncs) else: - logger.info("Frame for %s is empty", g) + logger.debug("Frame for %s is empty", g) else: - logger.info("Frame not found: %s", fname) + logger.debug("Frame not found: %s", fname) # From 3be3e2f91ad88de99038d8f09b5376b4812f8f2b Mon Sep 17 00:00:00 2001 From: Mark Conway Date: Tue, 23 May 2017 09:02:12 -0400 Subject: [PATCH 003/129] remove .bak files after Python2->3 conversion remove .bak files after Python2->3 conversion --- alphapy/alias.py.bak | 148 ---- alphapy/estimators.py.bak | 348 -------- alphapy/features.py.bak | 1635 ------------------------------------ alphapy/market_flow.py.bak | 393 --------- alphapy/model.py.bak | 1351 ----------------------------- alphapy/optimize.py.bak | 363 -------- alphapy/plots.py.bak | 1243 --------------------------- alphapy/portfolio.py.bak | 1164 ------------------------- alphapy/sport_flow.py.bak | 914 -------------------- 9 files changed, 7559 deletions(-) delete mode 100644 alphapy/alias.py.bak delete mode 100644 alphapy/estimators.py.bak delete mode 100644 alphapy/features.py.bak delete mode 100644 alphapy/market_flow.py.bak delete mode 100644 alphapy/model.py.bak delete mode 100644 alphapy/optimize.py.bak delete mode 100644 alphapy/plots.py.bak delete mode 100644 alphapy/portfolio.py.bak delete mode 100644 alphapy/sport_flow.py.bak diff --git a/alphapy/alias.py.bak b/alphapy/alias.py.bak deleted file mode 100644 index e8a1513..0000000 --- a/alphapy/alias.py.bak +++ /dev/null @@ -1,148 +0,0 @@ -################################################################################ -# -# Package : AlphaPy -# Module : alias -# Created : July 11, 2013 -# -# Copyright 2017 ScottFree Analytics LLC -# Mark Conway & Robert D. Scott II -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. -# -################################################################################ - - -# -# Imports -# - -import logging -import parser -import re - - -# -# Initialize logger -# - -logger = logging.getLogger(__name__) - - -# -# Class Alias -# - -class Alias(object): - """Create a new alias as a key-value pair. All aliases are stored - in ``Alias.aliases``. Duplicate keys or values are not allowed, - unless the ``replace`` parameter is ``True``. - - Parameters - ---------- - name : str - Alias key. - expr : str - Alias value. - replace : bool, optional - Replace the current key-value pair if it already exists. - - Attributes - ---------- - Alias.aliases : dict - Class variable for storing all known aliases - - Examples - -------- - - >>> Alias('atr', 'ma_truerange') - >>> Alias('hc', 'higher_close') - - """ - - # class variable to track all aliases - - aliases = {} - - # function __new__ - - def __new__(cls, - name, - expr, - replace = False): - # code - efound = expr in [Alias.aliases[key] for key in Alias.aliases] - if efound == True: - key = [key for key, aexpr in Alias.aliases.items() if aexpr == expr] - logger.info("Expression %s already exists for key %s", expr, key) - return - else: - if replace == True or not name in Alias.aliases: - identifier = re.compile(r"^[^\d\W]\w*\Z", re.UNICODE) - result1 = re.match(identifier, name) - if result1 is None: - logger.info("Invalid alias key: %s", name) - return - result2 = re.match(identifier, expr) - if result2 is None: - logger.info("Invalid alias expression: %s", expr) - return - return super(Alias, cls).__new__(cls) - else: - logger.info("Key %s already exists", name) - - # function __init__ - - def __init__(self, - name, - expr, - replace = False): - # code - self.name = name; - self.expr = expr; - # add key with expression - Alias.aliases[name] = expr - - # function __str__ - - def __str__(self): - return self.expr - - -# -# Function get_alias -# - -def get_alias(alias): - r"""Find an alias value with the given key. - - Parameters - ---------- - alias : str - Key for finding the alias value. - - Returns - ------- - alias_value : str - Value for the corresponding key. - - Examples - -------- - - >>> alias_value = get_alias('atr') - >>> alias_value = get_alias('hc') - - """ - if alias in Alias.aliases: - return Alias.aliases[alias] - else: - return None diff --git a/alphapy/estimators.py.bak b/alphapy/estimators.py.bak deleted file mode 100644 index c60212c..0000000 --- a/alphapy/estimators.py.bak +++ /dev/null @@ -1,348 +0,0 @@ -################################################################################ -# -# Package : AlphaPy -# Module : estimators -# Created : July 11, 2013 -# -# Copyright 2017 ScottFree Analytics LLC -# Mark Conway & Robert D. Scott II -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. -# -################################################################################ - - -# -# Imports -# - -from alphapy.globals import ModelType -from alphapy.globals import Objective -from alphapy.globals import SSEP - -import logging -import numpy as np -from scipy.stats import randint as sp_randint -from sklearn.ensemble import AdaBoostClassifier -from sklearn.ensemble import ExtraTreesClassifier -from sklearn.ensemble import ExtraTreesRegressor -from sklearn.ensemble import GradientBoostingClassifier -from sklearn.ensemble import GradientBoostingRegressor -from sklearn.ensemble import RandomForestClassifier -from sklearn.ensemble import RandomForestRegressor -from sklearn.linear_model import LinearRegression -from sklearn.linear_model import LogisticRegression -from sklearn.linear_model import RandomizedLasso -from sklearn.linear_model import RandomizedLogisticRegression -from sklearn.naive_bayes import GaussianNB -from sklearn.naive_bayes import MultinomialNB -from sklearn.neighbors import KNeighborsClassifier -from sklearn.neighbors import KNeighborsRegressor -from sklearn.svm import LinearSVC -from sklearn.svm import OneClassSVM -from sklearn.svm import SVC -import xgboost as xgb -import yaml - - -# -# Initialize logger -# - -logger = logging.getLogger(__name__) - - -# -# Define scorers -# - -scorers = {'accuracy' : (ModelType.classification, Objective.maximize), - 'average_precision' : (ModelType.classification, Objective.maximize), - 'f1' : (ModelType.classification, Objective.maximize), - 'f1_macro' : (ModelType.classification, Objective.maximize), - 'f1_micro' : (ModelType.classification, Objective.maximize), - 'f1_samples' : (ModelType.classification, Objective.maximize), - 'f1_weighted' : (ModelType.classification, Objective.maximize), - 'neg_log_loss' : (ModelType.classification, Objective.minimize), - 'precision' : (ModelType.classification, Objective.maximize), - 'recall' : (ModelType.classification, Objective.maximize), - 'roc_auc' : (ModelType.classification, Objective.maximize), - 'adjusted_rand_score' : (ModelType.clustering, Objective.maximize), - 'mean_absolute_error' : (ModelType.regression, Objective.minimize), - 'neg_mean_squared_error' : (ModelType.regression, Objective.minimize), - 'median_absolute_error' : (ModelType.regression, Objective.minimize), - 'r2' : (ModelType.regression, Objective.maximize)} - - -# -# Define XGB scoring map -# - -xgb_score_map = {'neg_log_loss' : 'logloss', - 'mean_absolute_error' : 'mae', - 'neg_mean_squared_error' : 'rmse', - 'precision' : 'map', - 'roc_auc' : 'auc'} - - -# -# Class Estimator -# - -class Estimator: - """Store information about each estimator. - - Parameters - ---------- - algorithm : str - Abbreviation representing the given algorithm. - model_type : enum ModelType - The machine learning task for this algorithm. - estimator : function - A scikit-learn, TensorFlow, or XGBoost function. - grid : dict - The dictionary of hyperparameters for grid search. - scoring : bool, optional - Use a scoring function to evaluate the best model. - - """ - - # __new__ - - def __new__(cls, - algorithm, - model_type, - estimator, - grid, - scoring=False): - return super(Estimator, cls).__new__(cls) - - # __init__ - - def __init__(self, - algorithm, - model_type, - estimator, - grid, - scoring=False): - self.algorithm = algorithm.upper() - self.model_type = model_type - self.estimator = estimator - self.grid = grid - self.scoring = scoring - - # __str__ - - def __str__(self): - return self.name - - -# -# Classes -# - -class AdaBoostClassifierCoef(AdaBoostClassifier): - """An AdaBoost classifier where the coefficients are set to - the feature importances for Recursive Feature Elimination - to work. - - """ - def fit(self, *args, **kwargs): - super(AdaBoostClassifierCoef, self).fit(*args, **kwargs) - self.coef_ = self.feature_importances_ - - -class ExtraTreesClassifierCoef(ExtraTreesClassifier): - """An Extra Trees classifier where the coefficients are set to - the feature importances for Recursive Feature Elimination - to work. - - """ - def fit(self, *args, **kwargs): - super(ExtraTreesClassifierCoef, self).fit(*args, **kwargs) - self.coef_ = self.feature_importances_ - - -class RandomForestClassifierCoef(RandomForestClassifier): - """A Random Forest classifier where the coefficients are set to - the feature importances for Recursive Feature Elimination - to work. - - """ - def fit(self, *args, **kwargs): - super(RandomForestClassifierCoef, self).fit(*args, **kwargs) - self.coef_ = self.feature_importances_ - -class GradientBoostingClassifierCoef(GradientBoostingClassifier): - """A Gradient Boostin classifier where the coefficients are set to - the feature importances for Recursive Feature Elimination - to work. - - """ - def fit(self, *args, **kwargs): - super(GradientBoostingClassifierCoef, self).fit(*args, **kwargs) - self.coef_ = self.feature_importances_ - - -# -# Define estimator map -# - -estimator_map = {'AB' : AdaBoostClassifierCoef, - 'GB' : GradientBoostingClassifierCoef, - 'GBR' : GradientBoostingRegressor, - 'KNN' : KNeighborsClassifier, - 'KNR' : KNeighborsRegressor, - 'LOGR' : LogisticRegression, - 'LR' : LinearRegression, - 'LSVC' : LinearSVC, - 'LSVM' : SVC, - 'NB' : MultinomialNB, - 'RBF' : SVC, - 'RF' : RandomForestClassifierCoef, - 'RFR' : RandomForestRegressor, - 'SVM' : SVC, - 'XGB' : xgb.XGBClassifier, - 'XGBM' : xgb.XGBClassifier, - 'XGBR' : xgb.XGBRegressor, - 'XT' : ExtraTreesClassifierCoef, - 'XTR' : ExtraTreesRegressor - } - - -# -# Function get_algos_config -# - -def get_algos_config(cfg_dir): - r"""Read the algorithms configuration file. - - Parameters - ---------- - cfg_dir : str - The directory where the configuration file ``algos.yml`` - is stored. - - Returns - ------- - specs : dict - The specifications for determining which algorithms to run. - - """ - - logger.info("Algorithm Configuration") - - # Read the configuration file - - full_path = SSEP.join([cfg_dir, 'algos.yml']) - with open(full_path, 'r') as ymlfile: - specs = yaml.load(ymlfile) - - # Ensure each algorithm has required keys - - required_keys = ['model_type', 'params', 'grid', 'scoring'] - for algo in specs: - algo_keys = specs[algo].keys() - if set(algo_keys) != set(required_keys): - logger.warning("Algorithm %s is missing the required keys %s", - algo, required_keys) - logger.warning("Keys found instead: %s", algo_keys) - else: - # determine whether or not model type is valid - model_types = {x.name: x.value for x in ModelType} - model_type = specs[algo]['model_type'] - if model_type in model_types: - specs[algo]['model_type'] = ModelType(model_types[model_type]) - else: - raise ValueError("algos.yml model:type %s unrecognized" % model_type) - - # Algorithm Specifications - return specs - - -# -# Function get_estimators -# - -# AdaBoost (feature_importances_) -# Gradient Boosting (feature_importances_) -# K-Nearest Neighbors (NA) -# Linear Regression (coef_) -# Linear Support Vector Machine (coef_) -# Logistic Regression (coef_) -# Naive Bayes (coef_) -# Radial Basis Function (NA) -# Random Forest (feature_importances_) -# Support Vector Machine (NA) -# XGBoost Binary (NA) -# XGBoost Multi (NA) -# Extra Trees (feature_importances_) -# Random Forest (feature_importances_) -# Randomized Lasso - -def get_estimators(model): - r"""Define all the AlphaPy estimators based on the contents - of the ``algos.yml`` file. - - Parameters - ---------- - model : alphapy.Model - The model object containing global AlphaPy parameters. - - Returns - ------- - estimators : dict - All of the estimators required for running the pipeline. - - """ - - # Extract model data - - directory = model.specs['directory'] - n_estimators = model.specs['n_estimators'] - n_jobs = model.specs['n_jobs'] - seed = model.specs['seed'] - verbosity = model.specs['verbosity'] - - # Initialize estimator dictionary - estimators = {} - - # Global parameter substitution fields - ps_fields = {'n_estimators' : 'n_estimators', - 'n_jobs' : 'n_jobs', - 'nthread' : 'n_jobs', - 'random_state' : 'seed', - 'seed' : 'seed', - 'verbose' : 'verbosity'} - - # Get algorithm specifications - - config_dir = SSEP.join([directory, 'config']) - algo_specs = get_algos_config(config_dir) - - # Create estimators for all of the algorithms - - for algo in algo_specs: - model_type = algo_specs[algo]['model_type'] - params = algo_specs[algo]['params'] - for param in params: - if param in ps_fields and isinstance(param, str): - algo_specs[algo]['params'][param] = eval(ps_fields[param]) - func = estimator_map[algo] - est = func(**params) - grid = algo_specs[algo]['grid'] - scoring = algo_specs[algo]['scoring'] - estimators[algo] = Estimator(algo, model_type, est, grid, scoring) - - # return the entire classifier list - return estimators diff --git a/alphapy/features.py.bak b/alphapy/features.py.bak deleted file mode 100644 index 70d397b..0000000 --- a/alphapy/features.py.bak +++ /dev/null @@ -1,1635 +0,0 @@ -################################################################################ -# -# Package : AlphaPy -# Module : features -# Created : July 11, 2013 -# -# Copyright 2017 ScottFree Analytics LLC -# Mark Conway & Robert D. Scott II -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. -# -################################################################################ - - -# -# Imports -# - -from alphapy.globals import BSEP, NULLTEXT, PSEP, SSEP, USEP -from alphapy.globals import Encoders -from alphapy.globals import ModelType -from alphapy.globals import Scalers -from alphapy.market_variables import Variable - -import category_encoders as ce -from importlib import import_module -from itertools import groupby -import logging -import math -import numpy as np -import pandas as pd -import re -from scipy import sparse -import scipy.stats as sps -from sklearn.cluster import MiniBatchKMeans -from sklearn.decomposition import PCA -from sklearn.feature_extraction.text import CountVectorizer -from sklearn.feature_extraction.text import TfidfTransformer -from sklearn.feature_selection import chi2 -from sklearn.feature_selection import f_classif -from sklearn.feature_selection import f_regression -from sklearn.feature_selection import SelectFdr -from sklearn.feature_selection import SelectFpr -from sklearn.feature_selection import SelectFwe -from sklearn.feature_selection import SelectKBest -from sklearn.feature_selection import SelectPercentile -from sklearn.feature_selection import VarianceThreshold -from sklearn.manifold import Isomap -from sklearn.manifold import TSNE -from sklearn.preprocessing import Imputer -from sklearn.preprocessing import MinMaxScaler -from sklearn.preprocessing import PolynomialFeatures -from sklearn.preprocessing import StandardScaler - - -# -# Initialize logger -# - -logger = logging.getLogger(__name__) - - -# -# Define feature scoring functions -# - -feature_scorers = {'f_classif' : f_classif, - 'chi2' : chi2, - 'f_regression' : f_regression, - 'SelectKBest' : SelectKBest, - 'SelectFpr' : SelectFpr, - 'SelectFdr' : SelectFdr, - 'SelectFwe' : SelectFwe} - - -# -# Function rtotal -# - -def rtotal(vec): - r"""Calculate the running total. - - Parameters - ---------- - vec : pandas.Series - The input array for calculating the running total. - - Returns - ------- - running_total : int - The final running total. - - Example - ------- - - >>> vec.rolling(window=20).apply(rtotal) - - """ - tcount = np.count_nonzero(vec) - fcount = len(vec) - tcount - running_total = tcount - fcount - return running_total - - -# -# Function runs -# - -def runs(vec): - r"""Calculate the total number of runs. - - Parameters - ---------- - vec : pandas.Series - The input array for calculating the number of runs. - - Returns - ------- - runs_value : int - The total number of runs. - - Example - ------- - - >>> vec.rolling(window=20).apply(runs) - - """ - runs_value = len(list(groupby(vec))) - return runs_value - - -# -# Function streak -# - -def streak(vec): - r"""Determine the length of the latest streak. - - Parameters - ---------- - vec : pandas.Series - The input array for calculating the latest streak. - - Returns - ------- - latest_streak : int - The length of the latest streak. - - Example - ------- - - >>> vec.rolling(window=20).apply(streak) - - """ - latest_streak = [len(list(g)) for k, g in groupby(vec)][-1] - return latest_streak - - -# -# Function zscore -# - -def zscore(vec): - r"""Calculate the Z-Score. - - Parameters - ---------- - vec : pandas.Series - The input array for calculating the Z-Score. - - Returns - ------- - zscore : float - The value of the Z-Score. - - References - ---------- - To calculate the Z-Score, you can find more information here [ZSCORE]_. - - .. [ZSCORE] https://en.wikipedia.org/wiki/Standard_score - - Example - ------- - - >>> vec.rolling(window=20).apply(zscore) - - """ - n1 = np.count_nonzero(vec) - n2 = len(vec) - n1 - fac1 = float(2 * n1 * n2) - fac2 = float(n1 + n2) - rbar = fac1 / fac2 + 1 - sr2num = fac1 * (fac1 - n1 - n2) - sr2den = math.pow(fac2, 2) * (fac2 - 1) - sr = math.sqrt(sr2num / sr2den) - if sr2den and sr: - zscore = (runs(vec) - rbar) / sr - else: - zscore = 0 - return zscore - - -# -# Function runs_test -# - -def runs_test(f, c, wfuncs, window): - r"""Perform a runs test on binary series. - - Parameters - ---------- - f : pandas.DataFrame - Dataframe containing the column ``c``. - c : str - Name of the column in the dataframe ``f``. - wfuncs : list - The set of runs test functions to apply to the column: - - ``'all'``: - Run all of the functions below. - ``'rtotal'``: - The running total over the ``window`` period. - ``'runs'``: - Total number of runs in ``window``. - ``'streak'``: - The length of the latest streak. - ``'zscore'``: - The Z-Score over the ``window`` period. - window : int - The rolling period. - - Returns - ------- - new_features : pandas.DataFrame - The dataframe containing the runs test features. - - References - ---------- - For more information about runs tests for detecting non-randomness, - refer to [RUNS]_. - - .. [RUNS] http://www.itl.nist.gov/div898/handbook/eda/section3/eda35d.htm - - """ - - fc = f[c] - all_funcs = {'runs' : runs, - 'streak' : streak, - 'rtotal' : rtotal, - 'zscore' : zscore} - # use all functions - if 'all' in wfuncs: - wfuncs = all_funcs.keys() - # apply each of the runs functions - new_features = pd.DataFrame() - for w in wfuncs: - if w in all_funcs: - new_feature = fc.rolling(window=window).apply(all_funcs[w]) - new_feature.fillna(0, inplace=True) - new_column_name = PSEP.join([c, w]) - new_feature = new_feature.rename(new_column_name) - frames = [new_features, new_feature] - new_features = pd.concat(frames, axis=1) - else: - logger.info("Runs Function %s not found", w) - return new_features - - -# -# Function split_to_letters -# - -def split_to_letters(f, c): - r"""Separate text into distinct characters. - - Parameters - ---------- - f : pandas.DataFrame - Dataframe containing the column ``c``. - c : str - Name of the text column in the dataframe ``f``. - - Returns - ------- - new_feature : pandas.Series - The array containing the new feature. - - Example - ------- - The value 'abc' becomes 'a b c'. - - """ - fc = f[c] - new_feature = None - dtype = fc.dtypes - if dtype == 'object': - fc.fillna(NULLTEXT, inplace=True) - maxlen = fc.str.len().max() - if maxlen > 1: - new_feature = fc.apply(lambda x: BSEP.join(list(x))) - return new_feature - - -# -# Function texplode -# - -def texplode(f, c): - r"""Get dummy values for a text column. - - Parameters - ---------- - f : pandas.DataFrame - Dataframe containing the column ``c``. - c : str - Name of the text column in the dataframe ``f``. - - Returns - ------- - dummies : pandas.DataFrame - The dataframe containing the dummy variables. - - Example - ------- - - This function is useful for columns that appear to - have separate character codes but are consolidated - into a single column. Here, the column ``c`` is - transformed into five dummy variables. - - === === === === === === - c 0_a 1_x 1_b 2_x 2_z - === === === === === === - abz 1 0 1 0 1 - abz 1 0 1 0 1 - axx 1 1 0 1 0 - abz 1 0 1 0 1 - axz 1 1 0 0 1 - === === === === === === - - """ - fc = f[c] - maxlen = fc.str.len().max() - fc.fillna(maxlen * BSEP, inplace=True) - fpad = str().join(['{:', BSEP, '>', str(maxlen), '}']) - fcpad = fc.apply(fpad.format) - fcex = fcpad.apply(lambda x: pd.Series(list(x))) - dummies = pd.get_dummies(fcex) - return dummies - - -# -# Function cvectorize -# - -def cvectorize(f, c, n): - r"""Use the Count Vectorizer and TF-IDF Transformer. - - Parameters - ---------- - f : pandas.DataFrame - Dataframe containing the column ``c``. - c : str - Name of the text column in the dataframe ``f``. - n : int - The number of n-grams. - - Returns - ------- - new_features : sparse matrix - The transformed features. - - References - ---------- - To use count vectorization and TF-IDF, you can find more - information here [TFE]_. - - .. [TFE] http://scikit-learn.org/stable/modules/feature_extraction.html#text-feature-extraction - - """ - fc = f[c] - fc.fillna(BSEP, inplace=True) - cvect = CountVectorizer(ngram_range=[1, n], analyzer='char') - cfeat = cvect.fit_transform(fc) - tfidf_transformer = TfidfTransformer() - new_features = tfidf_transformer.fit_transform(cfeat).toarray() - return new_features - - -# -# Function apply_treatment -# - -def apply_treatment(fname, df, fparams): - r"""Apply a treatment function to a column of the dataframe. - - Parameters - ---------- - fname : str - Name of the column to be treated in the dataframe ``df``. - df : pandas.DataFrame - Dataframe containing the column ``fname``. - fparams : list - The module, function, and parameter list of the treatment - function - - Returns - ------- - new_features : pandas.DataFrame - The set of features after applying a treatment function. - - """ - # Extract the treatment parameter list - module = fparams[0] - func_name = fparams[1] - plist = fparams[2:] - # Import the external treatment function - ext_module = import_module(module) - func = getattr(ext_module, func_name) - # Prepend the parameter list with the data frame and feature name - plist.insert(0, fname) - plist.insert(0, df) - # Apply the treatment - logger.info("Applying function %s from module %s to feature %s", - func_name, module, fname) - return func(*plist) - - -# -# Function apply_treatments -# - -def apply_treatments(model, X): - r"""Apply special functions to the original features. - - Parameters - ---------- - model : alphapy.Model - Model specifications indicating any treatments. - X : pandas.DataFrame - Combined train and test data, or just prediction data. - - Returns - ------- - all_features : pandas.DataFrame - All features, including treatments. - - Raises - ------ - IndexError - The number of treatment rows must match the number of - rows in ``X``. - - """ - - # Extract model parameters - treatments = model.specs['treatments'] - - # Log input parameters - - logger.info("Original Features : %s", X.columns) - logger.info("Feature Count : %d", X.shape[1]) - - # Iterate through columns, dispatching and transforming each feature. - - logger.info("Applying Treatments") - all_features = X - - for fname in X: - if treatments and fname in treatments: - features = apply_treatment(fname, X, treatments[fname]) - if features is not None: - if features.shape[0] == X.shape[0]: - all_features = pd.concat([all_features, features], axis=1) - else: - raise IndexError("The number of treatment rows [%d] must match X [%d]" % - (features.shape[0], X.shape[0])) - else: - logger.info("Could not apply treatment for feature %s", fname) - - logger.info("New Feature Count : %d", all_features.shape[1]) - - # Return all transformed training and test features - return all_features - - -# -# Function impute_values -# - -def impute_values(features, dt, sentinel): - r"""Impute values for a given data type. The *median* strategy - is applied for floating point values, and the *most frequent* - strategy is applied for integer or Boolean values. - - Parameters - ---------- - features : pandas.DataFrame - Dataframe containing the features for imputation. - dt : str - The values ``'float64'``, ``'int64'``, or ``'bool'``. - sentinel : float - The number to be imputed for NaN values. - - Returns - ------- - imputed_features : numpy array - The features after imputation. - - Raises - ------ - TypeError - Data type ``dt`` is invalid for imputation. - - References - ---------- - You can find more information on feature imputation here [IMP]_. - - .. [IMP] http://scikit-learn.org/stable/modules/preprocessing.html#imputation - - """ - try: - nfeatures = features.shape[1] - except: - features = features.values.reshape(-1, 1) - if dt == 'float64': - imp = Imputer(missing_values='NaN', strategy='median', axis=0) - elif dt == 'int64' or dt == 'bool': - imp = Imputer(missing_values='NaN', strategy='most_frequent', axis=0) - else: - raise TypeError("Data Type %s is invalid for imputation" % dt) - imputed = imp.fit_transform(features) - if imputed.shape[1] == 0: - nans = np.isnan(features) - features[nans] = sentinel - imputed_features = features - else: - imputed_features = imputed - return imputed_features - - -# -# Function get_numerical_features -# - -def get_numerical_features(fnum, fname, df, nvalues, dt, - sentinel, logt, plevel): - r"""Transform numerical features with imputation and possibly - log-transformation. - - Parameters - ---------- - fnum : int - Feature number, strictly for logging purposes - fname : str - Name of the numerical column in the dataframe ``df``. - df : pandas.DataFrame - Dataframe containing the column ``fname``. - nvalues : int - The number of unique values. - dt : str - The values ``'float64'``, ``'int64'``, or ``'bool'``. - sentinel : float - The number to be imputed for NaN values. - logt : bool - If ``True``, then log-transform numerical values. - plevel : float - The p-value threshold to test if a feature is normally distributed. - - Returns - ------- - new_values : numpy array - The set of imputed and transformed features. - - """ - feature = df[fname] - if len(feature) == nvalues: - logger.info("Feature %d: %s is a numerical feature of type %s with maximum number of values %d", - fnum, fname, dt, nvalues) - else: - logger.info("Feature %d: %s is a numerical feature of type %s with %d unique values", - fnum, fname, dt, nvalues) - # imputer for float, integer, or boolean data types - new_values = impute_values(feature, dt, sentinel) - # log-transform any values that do not fit a normal distribution - if logt and np.all(new_values > 0): - stat, pvalue = sps.normaltest(new_values) - if pvalue <= plevel: - logger.info("Feature %d: %s is not normally distributed [p-value: %f]", - fnum, fname, pvalue) - new_values = np.log(new_values) - return new_values - - -# -# Function get_polynomials -# - -def get_polynomials(features, poly_degree): - r"""Generate interactions that are products of distinct features. - - Parameters - ---------- - features : pandas.DataFrame - Dataframe containing the features for generating interactions. - poly_degree : int - The degree of the polynomial features. - - Returns - ------- - poly_features : numpy array - The interaction features only. - - References - ---------- - You can find more information on polynomial interactions here [POLY]_. - - .. [POLY] http://scikit-learn.org/stable/modules/generated/sklearn.preprocessing.PolynomialFeatures.html - - """ - polyf = PolynomialFeatures(interaction_only=True, - degree=poly_degree, - include_bias=False) - poly_features = polyf.fit_transform(features) - return poly_features - - -# -# Function get_text_features -# - -def get_text_features(fnum, fname, df, nvalues, vectorize, ngrams_max): - r"""Transform text features with count vectorization and TF-IDF, - or alternatively factorization. - - Parameters - ---------- - fnum : int - Feature number, strictly for logging purposes - fname : str - Name of the text column in the dataframe ``df``. - df : pandas.DataFrame - Dataframe containing the column ``fname``. - nvalues : int - The number of unique values. - vectorize : bool - If ``True``, then attempt count vectorization. - ngrams_max : int - The maximum number of n-grams for count vectorization. - - Returns - ------- - new_features : numpy array - The vectorized or factorized text features. - - References - ---------- - To use count vectorization and TF-IDF, you can find more - information here [TFE]_. - - """ - feature = df[fname] - min_length = int(feature.str.len().min()) - max_length = int(feature.str.len().max()) - if len(feature) == nvalues: - logger.info("Feature %d: %s is a text feature [%d:%d] with maximum number of values %d", - fnum, fname, min_length, max_length, nvalues) - else: - logger.info("Feature %d: %s is a text feature [%d:%d] with %d unique values", - fnum, fname, min_length, max_length, nvalues) - # need a null text placeholder for vectorization - feature.fillna(value=NULLTEXT, inplace=True) - # vectorization creates many columns, otherwise just factorize - if vectorize: - logger.info("Feature %d: %s => Attempting Vectorization", fnum, fname) - count_vect = CountVectorizer(ngram_range=[1, ngrams_max]) - try: - count_feature = count_vect.fit_transform(feature) - tfidf_transformer = TfidfTransformer() - new_features = tfidf_transformer.fit_transform(count_feature).todense() - logger.info("Feature %d: %s => Vectorization Succeeded", fnum, fname) - except: - logger.info("Feature %d: %s => Vectorization Failed", fnum, fname) - new_features, uniques = pd.factorize(feature) - else: - logger.info("Feature %d: %s => Factorization", fnum, fname) - new_features, uniques = pd.factorize(feature) - return new_features - - -# -# Function float_factor -# - -def float_factor(x, rounding): - r"""Convert a floating point number to a factor. - - Parameters - ---------- - x : float - The value to convert to a factor. - rounding : int - The number of places to round. - - Returns - ------- - ffactor : int - The resulting factor. - - """ - num2str = '{0:.{1}f}'.format - fstr = re.sub("[^0-9]", "", num2str(x, rounding)) - ffactor = int(fstr) if len(fstr) > 0 else 0 - return ffactor - - -# -# Function create_crosstabs -# - -def create_crosstabs(model): - r"""Create cross-tabulations for categorical variables. - - Parameters - ---------- - model : alphapy.Model - The model object containing the data. - - Returns - ------- - model : alphapy.Model - The model object with the updated feature map. - - """ - - logger.info("Creating Cross-Tabulations") - - # Extract model data - X = model.X_train - y = model.y_train - - # Extract model parameters - - factors = model.specs['factors'] - target_value = model.specs['target_value'] - - # Iterate through columns, dispatching and transforming each feature. - - crosstabs = {} - for fname in X: - if fname in factors: - logger.info("Creating crosstabs for feature %s", fname) - ct = pd.crosstab(X[fname], y).apply(lambda r : r / r.sum(), axis=1) - crosstabs[fname] = ct - - # Save crosstabs to the feature map - - model.feature_map['crosstabs'] = crosstabs - return model - - -# -# Function get_factors -# - -def get_factors(model, df, fnum, fname, nvalues, dtype, - encoder, rounding, sentinel): - r"""Convert the original feature to a factor. - - Parameters - ---------- - model : alphapy.Model - Model object with the feature specifications. - df : pandas.DataFrame - Dataframe containing the column ``fname``. - fnum : int - Feature number, strictly for logging purposes - fname : str - Name of the text column in the dataframe ``df``. - nvalues : int - The number of unique values. - dtype : str - The values ``'float64'``, ``'int64'``, or ``'bool'``. - encoder : alphapy.features.Encoders - Type of encoder to apply. - rounding : int - Number of places to round. - sentinel : float - The number to be imputed for NaN values. - - Returns - ------- - all_features : numpy array - The features that have been transformed to factors. - - """ - - logger.info("Feature %d: %s is a factor of type %s with %d unique values", - fnum, fname, dtype, nvalues) - logger.info("Encoding: %s", encoder) - - # Extract model data - - feature_map = model.feature_map - model_type = model.specs['model_type'] - target_value = model.specs['target_value'] - - # get feature - feature = df[fname] - # convert float to factor - if dtype == 'float64': - logger.info("Rounding: %d", rounding) - feature = feature.apply(float_factor, args=[rounding]) - # encoders - enc = None - ef = pd.DataFrame(feature) - if encoder == Encoders.factorize: - pd_factors = pd.factorize(feature)[0] - pd_features = pd.DataFrame(pd_factors) - elif encoder == Encoders.onehot: - pd_features = pd.get_dummies(feature) - elif encoder == Encoders.ordinal: - enc = ce.OrdinalEncoder(cols=[fname]) - elif encoder == Encoders.binary: - enc = ce.BinaryEncoder(cols=[fname]) - elif encoder == Encoders.helmert: - enc = ce.HelmertEncoder(cols=[fname]) - elif encoder == Encoders.sumcont: - enc = ce.SumEncoder(cols=[fname]) - elif encoder == Encoders.polynomial: - enc = ce.PolynomialEncoder(cols=[fname]) - elif encoder == Encoders.backdiff: - enc = ce.BackwardDifferenceEncoder(cols=[fname]) - else: - raise ValueError("Unknown Encoder %s" % encoder) - # If encoding worked, calculate target percentages for classifiers. - pd_exists = not pd_features.empty - enc_exists = enc is not None - all_features = None - if pd_exists or enc_exists: - if pd_exists: - all_features = pd_features - elif enc_exists: - all_features = enc.fit_transform(ef, None) - # Calculate target percentages for factors - if (model_type == ModelType.classification and - fname in feature_map['crosstabs']): - # Get the crosstab for this feature - ct = feature_map['crosstabs'][fname] - # map target percentages to the new feature - ct_map = ct.to_dict()[target_value] - ct_feature = df[[fname]].applymap(ct_map.get) - # impute sentinel for any values that could not be mapped - ct_feature.fillna(value=sentinel, inplace=True) - # concatenate all generated features - all_features = np.column_stack((all_features, ct_feature)) - logger.info("Applied target percentages for %s", fname) - else: - raise RuntimeError("Encoding for feature %s failed" % fname) - return all_features - - -# -# Function create_numpy_features -# - -def create_numpy_features(base_features, sentinel): - r"""Calculate the sum, mean, standard deviation, and variance - of each row. - - Parameters - ---------- - base_features : numpy array - The feature dataframe. - sentinel : float - The number to be imputed for NaN values. - - Returns - ------- - np_features : numpy array - The calculated NumPy features. - - """ - - logger.info("Creating NumPy Features") - - # Calculate the total, mean, standard deviation, and variance. - - logger.info("NumPy Feature: sum") - row_sum = np.sum(base_features, axis=1) - logger.info("NumPy Feature: mean") - row_mean = np.mean(base_features, axis=1) - logger.info("NumPy Feature: standard deviation") - row_std = np.std(base_features, axis=1) - logger.info("NumPy Feature: variance") - row_var = np.var(base_features, axis=1) - - # Impute, scale, and stack all new features. - - np_features = np.column_stack((row_sum, row_mean, row_std, row_var)) - np_features = impute_values(np_features, 'float64', sentinel) - np_features = StandardScaler().fit_transform(np_features) - - # Return new NumPy features - - logger.info("NumPy Feature Count : %d", np_features.shape[1]) - return np_features - - -# -# Function create_scipy_features -# - -def create_scipy_features(base_features, sentinel): - r"""Calculate the skew, kurtosis, and other statistical features - for each row. - - Parameters - ---------- - base_features : numpy array - The feature dataframe. - sentinel : float - The number to be imputed for NaN values. - - Returns - ------- - sp_features : numpy array - The calculated SciPy features. - - """ - - logger.info("Creating SciPy Features") - - # Generate scipy features - - logger.info("SciPy Feature: geometric mean") - row_gmean = sps.gmean(base_features, axis=1) - logger.info("SciPy Feature: kurtosis") - row_kurtosis = sps.kurtosis(base_features, axis=1) - logger.info("SciPy Feature: kurtosis test") - row_ktest, pvalue = sps.kurtosistest(base_features, axis=1) - logger.info("SciPy Feature: normal test") - row_normal, pvalue = sps.normaltest(base_features, axis=1) - logger.info("SciPy Feature: skew") - row_skew = sps.skew(base_features, axis=1) - logger.info("SciPy Feature: skew test") - row_stest, pvalue = sps.skewtest(base_features, axis=1) - logger.info("SciPy Feature: variation") - row_var = sps.variation(base_features, axis=1) - logger.info("SciPy Feature: signal-to-noise ratio") - row_stn = sps.signaltonoise(base_features, axis=1) - logger.info("SciPy Feature: standard error of mean") - row_sem = sps.sem(base_features, axis=1) - - sp_features = np.column_stack((row_gmean, row_kurtosis, row_ktest, - row_normal, row_skew, row_stest, - row_var, row_stn, row_sem)) - sp_features = impute_values(sp_features, 'float64', sentinel) - sp_features = StandardScaler().fit_transform(sp_features) - - # Return new SciPy features - - logger.info("SciPy Feature Count : %d", sp_features.shape[1]) - return sp_features - - -# -# Function create_clusters -# - -def create_clusters(features, model): - r"""Cluster the given features. - - Parameters - ---------- - features : numpy array - The features to cluster. - model : alphapy.Model - The model object with the clustering parameters. - - Returns - ------- - cfeatures : numpy array - The calculated clusters. - - References - ---------- - You can find more information on clustering here [CLUS]_. - - .. [CLUS] http://scikit-learn.org/stable/modules/clustering.html - - """ - - logger.info("Creating Clustering Features") - - # Extract model parameters - - cluster_inc = model.specs['cluster_inc'] - cluster_max = model.specs['cluster_max'] - cluster_min = model.specs['cluster_min'] - n_jobs = model.specs['n_jobs'] - seed = model.specs['seed'] - - # Log model parameters - - logger.info("Cluster Minimum : %d", cluster_min) - logger.info("Cluster Maximum : %d", cluster_max) - logger.info("Cluster Increment : %d", cluster_inc) - - # Generate clustering features - - cfeatures = np.zeros((features.shape[0], 1)) - for i in range(cluster_min, cluster_max+1, cluster_inc): - logger.info("k = %d", i) - km = MiniBatchKMeans(n_clusters=i, random_state=seed) - km.fit(features) - labels = km.predict(features) - labels = labels.reshape(-1, 1) - cfeatures = np.column_stack((cfeatures, labels)) - cfeatures = np.delete(cfeatures, 0, axis=1) - - # Return new clustering features - - logger.info("Clustering Feature Count : %d", cfeatures.shape[1]) - return cfeatures - - -# -# Function create_pca_features -# - -def create_pca_features(features, model): - r"""Apply Principal Component Analysis (PCA) to the features. - - Parameters - ---------- - features : numpy array - The input features. - model : alphapy.Model - The model object with the PCA parameters. - - Returns - ------- - pfeatures : numpy array - The PCA features. - - References - ---------- - You can find more information on Principal Component Analysis here [PCA]_. - - .. [PCA] http://scikit-learn.org/stable/modules/decomposition.html#pca - - """ - - logger.info("Creating PCA Features") - - # Extract model parameters - - pca_inc = model.specs['pca_inc'] - pca_max = model.specs['pca_max'] - pca_min = model.specs['pca_min'] - pca_whiten = model.specs['pca_whiten'] - - # Log model parameters - - logger.info("PCA Minimum : %d", pca_min) - logger.info("PCA Maximum : %d", pca_max) - logger.info("PCA Increment : %d", pca_inc) - logger.info("PCA Whitening : %r", pca_whiten) - - # Generate clustering features - - pfeatures = np.zeros((features.shape[0], 1)) - for i in range(pca_min, pca_max+1, pca_inc): - logger.info("n_components = %d", i) - X_pca = PCA(n_components=i, whiten=pca_whiten).fit_transform(features) - pfeatures = np.column_stack((pfeatures, X_pca)) - pfeatures = np.delete(pfeatures, 0, axis=1) - - # Return new clustering features - - logger.info("PCA Feature Count : %d", pfeatures.shape[1]) - return pfeatures - - -# -# Function create_isomap_features -# - -def create_isomap_features(features, model): - r"""Create Isomap features. - - Parameters - ---------- - features : numpy array - The input features. - model : alphapy.Model - The model object with the Isomap parameters. - - Returns - ------- - ifeatures : numpy array - The Isomap features. - - Notes - ----- - - Isomaps are very memory-intensive. Your process will be killed - if you run out of memory. - - References - ---------- - You can find more information on Principal Component Analysis here [ISO]_. - - .. [ISO] http://scikit-learn.org/stable/modules/manifold.html#isomap - - """ - - logger.info("Creating Isomap Features") - - # Extract model parameters - - iso_components = model.specs['iso_components'] - iso_neighbors = model.specs['iso_neighbors'] - n_jobs = model.specs['n_jobs'] - - # Log model parameters - - logger.info("Isomap Components : %d", iso_components) - logger.info("Isomap Neighbors : %d", iso_neighbors) - - # Generate Isomap features - - model = Isomap(n_neighbors=iso_neighbors, n_components=iso_components, - n_jobs=n_jobs) - ifeatures = model.fit_transform(features) - - # Return new Isomap features - - logger.info("Isomap Feature Count : %d", ifeatures.shape[1]) - return ifeatures - - -# -# Function create_tsne_features -# - -def create_tsne_features(features, model): - r"""Create t-SNE features. - - Parameters - ---------- - features : numpy array - The input features. - model : alphapy.Model - The model object with the t-SNE parameters. - - Returns - ------- - tfeatures : numpy array - The t-SNE features. - - References - ---------- - You can find more information on the t-SNE technique here [TSNE]_. - - .. [TSNE] http://scikit-learn.org/stable/modules/manifold.html#t-distributed-stochastic-neighbor-embedding-t-sne - - """ - - logger.info("Creating T-SNE Features") - - # Extract model parameters - - seed = model.specs['seed'] - tsne_components = model.specs['tsne_components'] - tsne_learn_rate = model.specs['tsne_learn_rate'] - tsne_perplexity = model.specs['tsne_perplexity'] - - # Log model parameters - - logger.info("T-SNE Components : %d", tsne_components) - logger.info("T-SNE Learning Rate : %d", tsne_learn_rate) - logger.info("T-SNE Perplexity : %d", tsne_perplexity) - - # Generate T-SNE features - - model = TSNE(n_components=tsne_components, perplexity=tsne_perplexity, - learning_rate=tsne_learn_rate, random_state=seed) - tfeatures = model.fit_transform(features) - - # Return new T-SNE features - - logger.info("T-SNE Feature Count : %d", tfeatures.shape[1]) - return tfeatures - - -# -# Function create_features -# - -def create_features(model, X): - r"""Create features for the train and test set. - - Parameters - ---------- - model : alphapy.Model - Model object with the feature specifications. - X : pandas.DataFrame - Combined train and test data. - - Returns - ------- - all_features : numpy array - The new features. - - Raises - ------ - TypeError - Unrecognized data type. - - """ - - # Extract model parameters - - clustering = model.specs['clustering'] - counts_flag = model.specs['counts'] - encoder = model.specs['encoder'] - factors = model.specs['factors'] - isomap = model.specs['isomap'] - logtransform = model.specs['logtransform'] - model_type = model.specs['model_type'] - ngrams_max = model.specs['ngrams_max'] - numpy_flag = model.specs['numpy'] - pca = model.specs['pca'] - pvalue_level = model.specs['pvalue_level'] - rounding = model.specs['rounding'] - scaling = model.specs['scaler_option'] - scaler = model.specs['scaler_type'] - scipy_flag = model.specs['scipy'] - sentinel = model.specs['sentinel'] - target_value = model.specs['target_value'] - tsne = model.specs['tsne'] - vectorize = model.specs['vectorize'] - - # Log input parameters - - logger.info("Original Features : %s", X.columns) - logger.info("Feature Count : %d", X.shape[1]) - - # Set classification flag - - classify = True if model_type == ModelType.classification else False - - # Count zero and NaN values - - if counts_flag: - logger.info("Creating Count Features") - logger.info("NA Counts") - X['nan_count'] = X.count(axis=1) - logger.info("Number Counts") - for i in range(10): - fc = USEP.join(['count', str(i)]) - X[fc] = (X == i).astype(int).sum(axis=1) - logger.info("New Feature Count : %d", X.shape[1]) - - # Iterate through columns, dispatching and transforming each feature. - - logger.info("Creating Base Features") - all_features = np.zeros((X.shape[0], 1)) - - for i, fc in enumerate(X): - fnum = i + 1 - dtype = X[fc].dtypes - nunique = len(X[fc].unique()) - # standard processing of numerical, categorical, and text features - if fc in factors: - features = get_factors(model, X, fnum, fc, nunique, dtype, - encoder, rounding, sentinel) - elif dtype == 'float64' or dtype == 'int64' or dtype == 'bool': - features = get_numerical_features(fnum, fc, X, nunique, dtype, - sentinel, logtransform, pvalue_level) - elif dtype == 'object': - features = get_text_features(fnum, fc, X, nunique, vectorize, ngrams_max) - else: - raise TypeError("Base Feature Error with unrecognized type %s" % dtype) - if features.shape[0] == all_features.shape[0]: - all_features = np.column_stack((all_features, features)) - else: - logger.info("Feature %s has the wrong number of rows: %d", - fc, features.shape[0]) - all_features = np.delete(all_features, 0, axis=1) - - logger.info("New Feature Count : %d", all_features.shape[1]) - - # Call standard scaler for all features - - if scaling: - logger.info("Scaling Base Features") - if scaler == Scalers.standard: - all_features = StandardScaler().fit_transform(all_features) - elif scaler == Scalers.minmax: - all_features = MinMaxScaler().fit_transform(all_features) - else: - logger.info("Unrecognized scaler: %s", scaler) - else: - logger.info("Skipping Scaling") - - # Perform dimensionality reduction only on base feature set - base_features = all_features - - # Calculate the total, mean, standard deviation, and variance - - if numpy_flag: - np_features = create_numpy_features(base_features, sentinel) - all_features = np.column_stack((all_features, np_features)) - logger.info("New Feature Count : %d", all_features.shape[1]) - - # Generate scipy features - - if scipy_flag: - sp_features = create_scipy_features(base_features, sentinel) - all_features = np.column_stack((all_features, sp_features)) - logger.info("New Feature Count : %d", all_features.shape[1]) - - # Create clustering features - - if clustering: - cfeatures = create_clusters(base_features, model) - all_features = np.column_stack((all_features, cfeatures)) - logger.info("New Feature Count : %d", all_features.shape[1]) - - # Create PCA features - - if pca: - pfeatures = create_pca_features(base_features, model) - all_features = np.column_stack((all_features, pfeatures)) - logger.info("New Feature Count : %d", all_features.shape[1]) - - # Create Isomap features - - if isomap: - ifeatures = create_isomap_features(base_features, model) - all_features = np.column_stack((all_features, ifeatures)) - logger.info("New Feature Count : %d", all_features.shape[1]) - - # Create T-SNE features - - if tsne: - tfeatures = create_tsne_features(base_features, model) - all_features = np.column_stack((all_features, tfeatures)) - logger.info("New Feature Count : %d", all_features.shape[1]) - - # Return all transformed training and test features - return all_features - - -# -# Function select_features -# - -def select_features(model): - r"""Select features with univariate selection. - - Parameters - ---------- - model : alphapy.Model - Model object with the feature selection specifications. - - Returns - ------- - model : alphapy.Model - Model object with the revised number of features. - - References - ---------- - You can find more information on univariate feature selection here [UNI]_. - - .. [UNI] http://scikit-learn.org/stable/modules/feature_selection.html#univariate-feature-selection - - """ - - logger.info("Feature Selection") - - # Extract model data. - - X_train = model.X_train - y_train = model.y_train - - # Extract model parameters. - - fs_percentage = model.specs['fs_percentage'] - fs_score_func = model.specs['fs_score_func'] - - # Select top features based on percentile. - - fs = SelectPercentile(score_func=fs_score_func, - percentile=fs_percentage) - - # Perform feature selection and get the support mask - - fsfit = fs.fit(X_train, y_train) - support = fsfit.get_support() - - # Record the support vector - - logger.info("Saving Univariate Support") - model.feature_map['uni_support'] = support - - # Record the support vector - - X_train_new = model.X_train[:, support] - X_test_new = model.X_test[:, support] - - # Count the number of new features. - - logger.info("Old Feature Count : %d", X_train.shape[1]) - logger.info("New Feature Count : %d", X_train_new.shape[1]) - - # Store the reduced features in the model. - - model.X_train = X_train_new - model.X_test = X_test_new - - # Return the modified model - return model - - -# -# Function save_features -# - -def save_features(model, X_train, X_test, y_train=None, y_test=None): - r"""Save new features to the model. - - Parameters - ---------- - model : alphapy.Model - Model object with train and test data. - X_train : numpy array - Training features. - X_test : numpy array - Testing features. - y_train : numpy array - Training labels. - y_test : numpy array - Testing labels. - - Returns - ------- - model : alphapy.Model - Model object with new train and test data. - - """ - - logger.info("Saving New Features in Model") - - model.X_train = X_train - model.X_test = X_test - if y_train is not None: - model.y_train = y_train - if y_test is not None: - model.y_test = y_test - - return model - - -# -# Function create_interactions -# - -def create_interactions(model, X): - r"""Create feature interactions based on the model specifications. - - Parameters - ---------- - model : alphapy.Model - Model object with train and test data. - X : numpy array - Feature Matrix. - - Returns - ------- - all_features : numpy array - The new interaction features. - - Raises - ------ - TypeError - Unknown model type when creating interactions. - - """ - - logger.info("Creating Interactions") - - # Extract model parameters - - interactions = model.specs['interactions'] - isample_pct = model.specs['isample_pct'] - model_type = model.specs['model_type'] - n_jobs = model.specs['n_jobs'] - poly_degree = model.specs['poly_degree'] - predict_mode = model.specs['predict_mode'] - seed = model.specs['seed'] - verbosity = model.specs['verbosity'] - - # Extract model data - - X_train = model.X_train - y_train = model.y_train - - # Log parameters - logger.info("Initial Feature Count : %d", X.shape[1]) - - # Initialize all features - all_features = X - - # Get polynomial features - - if interactions: - if not predict_mode: - logger.info("Generating Polynomial Features") - logger.info("Interaction Percentage : %d", isample_pct) - logger.info("Polynomial Degree : %d", poly_degree) - if model_type == ModelType.regression: - selector = SelectPercentile(f_regression, percentile=isample_pct) - elif model_type == ModelType.classification: - selector = SelectPercentile(f_classif, percentile=isample_pct) - else: - raise TypeError("Unknown model type when creating interactions") - selector.fit(X_train, y_train) - support = selector.get_support() - model.feature_map['poly_support'] = support - else: - support = model.feature_map['poly_support'] - pfeatures = get_polynomials(X[:, support], poly_degree) - logger.info("Polynomial Feature Count : %d", pfeatures.shape[1]) - pfeatures = StandardScaler().fit_transform(pfeatures) - all_features = np.hstack((all_features, pfeatures)) - logger.info("New Total Feature Count : %d", all_features.shape[1]) - else: - logger.info("Skipping Interactions") - - # Return all features - return all_features - - -# -# Function drop_features -# - -def drop_features(X, drop): - r"""Drop any specified features. - - Parameters - ---------- - X : pandas.DataFrame - The dataframe containing the features. - drop : list - The list of features to remove from ``X``. - - Returns - ------- - X : pandas.DataFrame - The dataframe without the dropped features. - - """ - X.drop(drop, axis=1, inplace=True, errors='ignore') - return X - - -# -# Function remove_lv_features -# - -def remove_lv_features(model, X): - r"""Remove low-variance features. - - Parameters - ---------- - model : alphapy.Model - Model specifications for removing features. - X : numpy array - The feature matrix. - - Returns - ------- - X_reduced : numpy array - The reduced feature matrix. - - References - ---------- - You can find more information on low-variance feature selection here [LV]_. - - .. [LV] http://scikit-learn.org/stable/modules/feature_selection.html#variance-threshold - - """ - - logger.info("Removing Low-Variance Features") - - # Extract model parameters - - lv_remove = model.specs['lv_remove'] - lv_threshold = model.specs['lv_threshold'] - predict_mode = model.specs['predict_mode'] - - # Remove low-variance features - - if lv_remove: - logger.info("Low-Variance Threshold : %.2f", lv_threshold) - logger.info("Original Feature Count : %d", X.shape[1]) - if not predict_mode: - selector = VarianceThreshold(threshold=lv_threshold) - selector.fit(X) - support = selector.get_support() - model.feature_map['lv_support'] = support - else: - support = model.feature_map['lv_support'] - X_reduced = X[:, support] - logger.info("Reduced Feature Count : %d", X_reduced.shape[1]) - else: - X_reduced = X - logger.info("Skipping Low-Variance Features") - - return X_reduced diff --git a/alphapy/market_flow.py.bak b/alphapy/market_flow.py.bak deleted file mode 100644 index 6fe6b0b..0000000 --- a/alphapy/market_flow.py.bak +++ /dev/null @@ -1,393 +0,0 @@ -################################################################################ -# -# Package : AlphaPy -# Module : market_flow -# Created : July 11, 2013 -# -# Copyright 2017 ScottFree Analytics LLC -# Mark Conway & Robert D. Scott II -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. -# -################################################################################ - - -# -# Imports -# - -from alphapy.alias import Alias -from alphapy.analysis import Analysis -from alphapy.analysis import run_analysis -from alphapy.data import get_feed_data -from alphapy.globals import PSEP, SSEP -from alphapy.group import Group -from alphapy.market_variables import Variable -from alphapy.market_variables import vmapply -from alphapy.model import get_model_config -from alphapy.model import Model -from alphapy.portfolio import gen_portfolio -from alphapy.space import Space -from alphapy.system import run_system -from alphapy.system import System -from alphapy.utilities import valid_date - -import argparse -import datetime -import logging -import os -import pandas as pd -import yaml - - -# -# Initialize logger -# - -logger = logging.getLogger(__name__) - - -# -# Function get_market_config -# - -def get_market_config(): - r"""Read the configuration file for MarketFlow. - - Parameters - ---------- - None : None - - Returns - ------- - specs : dict - The parameters for controlling MarketFlow. - - """ - - logger.info("MarketFlow Configuration") - - # Read the configuration file - - full_path = SSEP.join([PSEP, 'config', 'market.yml']) - with open(full_path, 'r') as ymlfile: - cfg = yaml.load(ymlfile) - - # Store configuration parameters in dictionary - - specs = {} - - # Section: market [this section must be first] - - specs['forecast_period'] = cfg['market']['forecast_period'] - specs['fractal'] = cfg['market']['fractal'] - specs['leaders'] = cfg['market']['leaders'] - specs['data_history'] = cfg['market']['data_history'] - specs['predict_history'] = cfg['market']['predict_history'] - specs['schema'] = cfg['market']['schema'] - specs['target_group'] = cfg['market']['target_group'] - - # Create the subject/schema/fractal namespace - - sspecs = ['stock', specs['schema'], specs['fractal']] - space = Space(*sspecs) - - # Section: features - - try: - logger.info("Getting Features") - specs['features'] = cfg['features'] - except: - logger.info("No Features Found") - specs['features'] = {} - - # Section: groups - - try: - logger.info("Defining Groups") - for g, m in cfg['groups'].items(): - Group(g, space) - Group.groups[g].add(m) - except: - logger.info("No Groups Found") - - # Section: aliases - - try: - logger.info("Defining Aliases") - for k, v in cfg['aliases'].items(): - Alias(k, v) - except: - logger.info("No Aliases Found") - - # Section: system - - try: - logger.info("Getting System Parameters") - specs['system'] = cfg['system'] - except: - logger.info("No System Parameters Found") - specs['system'] = {} - - # Section: variables - - try: - logger.info("Defining Variables") - for k, v in cfg['variables'].items(): - Variable(k, v) - except: - logger.info("No Variables Found") - - # Section: functions - - try: - logger.info("Getting Variable Functions") - specs['functions'] = cfg['functions'] - except: - logger.info("No Variable Functions Found") - specs['functions'] = {} - - # Log the stock parameters - - logger.info('MARKET PARAMETERS:') - logger.info('features = %s', specs['features']) - logger.info('forecast_period = %d', specs['forecast_period']) - logger.info('fractal = %s', specs['fractal']) - logger.info('leaders = %s', specs['leaders']) - logger.info('data_history = %d', specs['data_history']) - logger.info('predict_history = %s', specs['predict_history']) - logger.info('schema = %s', specs['schema']) - logger.info('system = %s', specs['system']) - logger.info('target_group = %s', specs['target_group']) - - # Market Specifications - return specs - - -# -# Function market_pipeline -# - -def market_pipeline(model, market_specs): - r"""AlphaPy MarketFlow Pipeline - - Parameters - ---------- - model : alphapy.Model - The model object for AlphaPy. - market_specs : dict - The specifications for controlling the MarketFlow pipeline. - - Returns - ------- - model : alphapy.Model - The final results are stored in the model object. - - Notes - ----- - (1) Define a group. - (2) Get the market data. - (3) Apply system features. - (4) Create an analysis. - (5) Run the analysis, which calls AlphaPy. - - """ - - logger.info("Running MarketFlow Pipeline") - - # Get any model specifications - - predict_mode = model.specs['predict_mode'] - target = model.specs['target'] - - # Get any market specifications - - data_history = market_specs['data_history'] - features = market_specs['features'] - forecast_period = market_specs['forecast_period'] - functions = market_specs['functions'] - leaders = market_specs['leaders'] - predict_history = market_specs['predict_history'] - target_group = market_specs['target_group'] - - # Get the system specifications - - system_specs = market_specs['system'] - if system_specs: - system_name = system_specs['name'] - try: - longshort = True - longentry = system_specs['longentry'] - shortentry = system_specs['shortentry'] - longexit = system_specs['longexit'] - shortexit = system_specs['shortexit'] - holdperiod = system_specs['holdperiod'] - scale = system_specs['scale'] - logger.info("Running Long/Short System %s", system_name) - except: - longshort = False - system_params = system_specs['params'] - logger.info("Running System %s", system_name) - - # Set the target group - - group = Group.groups[target_group] - logger.info("All Members: %s", group.members) - - # Get stock data - - lookback = predict_history if predict_mode else data_history - daily = get_feed_data(group, lookback) - - # Apply the features to all of the frames - - vmapply(group, features, functions) - vmapply(group, [target], functions) - - # Run a system or an analysis - - if system_specs: - # create and run the system - if longshort: - system_ls = System(system_name, longentry, shortentry, - longexit, shortexit, holdperiod, scale) - tfs = run_system(model, system_ls, group) - else: - tfs = run_system(model, system_name, group, system_params) - # generate a portfolio - gen_portfolio(model, system_name, group, tfs) - else: - # run the analysis, including the model pipeline - a = Analysis(model, group) - results = run_analysis(a, forecast_period, leaders, predict_history) - - # Return the completed model - return model - - -# -# Function main -# - -def main(args=None): - r"""MarketFlow Main Program - - Notes - ----- - (1) Initialize logging. - (2) Parse the command line arguments. - (3) Get the market configuration. - (4) Get the model configuration. - (5) Create the model object. - (6) Call the main MarketFlow pipeline. - - Raises - ------ - ValueError - Training date must be before prediction date. - - """ - - # Logging - - logging.basicConfig(format="[%(asctime)s] %(levelname)s\t%(message)s", - filename="market_flow.log", filemode='a', level=logging.DEBUG, - datefmt='%m/%d/%y %H:%M:%S') - formatter = logging.Formatter("[%(asctime)s] %(levelname)s\t%(message)s", - datefmt='%m/%d/%y %H:%M:%S') - console = logging.StreamHandler() - console.setFormatter(formatter) - console.setLevel(logging.INFO) - logging.getLogger().addHandler(console) - - # Start the pipeline - - logger.info('*'*80) - logger.info("MarketFlow Start") - logger.info('*'*80) - - # Argument Parsing - - parser = argparse.ArgumentParser(description="MarketFlow Parser") - parser.add_argument('--pdate', dest='predict_date', - help="prediction date is in the format: YYYY-MM-DD", - required=False, type=valid_date) - parser.add_argument('--tdate', dest='train_date', - help="training date is in the format: YYYY-MM-DD", - required=False, type=valid_date) - parser.add_mutually_exclusive_group(required=False) - parser.add_argument('--predict', dest='predict_mode', action='store_true') - parser.add_argument('--train', dest='predict_mode', action='store_false') - parser.set_defaults(predict_mode=False) - args = parser.parse_args() - - # Set train and predict dates - - if args.train_date: - train_date = args.train_date - else: - train_date = pd.datetime(1900, 1, 1).strftime("%Y-%m-%d") - - if args.predict_date: - predict_date = args.predict_date - else: - predict_date = datetime.date.today().strftime("%Y-%m-%d") - - # Verify that the dates are in sequence. - - if train_date >= predict_date: - raise ValueError("Training date must be before prediction date") - else: - logger.info("Training Date: %s", train_date) - logger.info("Prediction Date: %s", predict_date) - - # Read stock configuration file - market_specs = get_market_config() - - # Read model configuration file - - model_specs = get_model_config() - model_specs['predict_mode'] = args.predict_mode - model_specs['predict_date'] = predict_date - model_specs['train_date'] = train_date - - # Create directories if necessary - - output_dirs = ['config', 'data', 'input', 'model', 'output', 'plots', 'systems'] - for od in output_dirs: - output_dir = SSEP.join([model_specs['directory'], od]) - if not os.path.exists(output_dir): - logger.info("Creating directory %s", output_dir) - os.makedirs(output_dir) - - # Create a model from the arguments - - logger.info("Creating Model") - model = Model(model_specs) - - # Start the pipeline - model = market_pipeline(model, market_specs) - - # Complete the pipeline - - logger.info('*'*80) - logger.info("MarketFlow End") - logger.info('*'*80) - - -# -# MAIN PROGRAM -# - -if __name__ == "__main__": - main() diff --git a/alphapy/model.py.bak b/alphapy/model.py.bak deleted file mode 100644 index a041971..0000000 --- a/alphapy/model.py.bak +++ /dev/null @@ -1,1351 +0,0 @@ -################################################################################ -# -# Package : AlphaPy -# Module : model -# Created : July 11, 2013 -# -# Copyright 2017 ScottFree Analytics LLC -# Mark Conway & Robert D. Scott II -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. -# -################################################################################ - - -# -# Imports -# - -from alphapy.estimators import scorers -from alphapy.estimators import xgb_score_map -from alphapy.features import feature_scorers -from alphapy.frame import read_frame -from alphapy.frame import write_frame -from alphapy.globals import Encoders -from alphapy.globals import ModelType -from alphapy.globals import Objective -from alphapy.globals import Partition, datasets -from alphapy.globals import PSEP, SSEP, USEP -from alphapy.globals import SamplingMethod -from alphapy.globals import Scalers -from alphapy.utilities import np_store_data - -from copy import copy -from datetime import datetime -import glob -import logging -import numpy as np -import os -import pandas as pd -from sklearn.calibration import CalibratedClassifierCV -from sklearn.externals import joblib -from sklearn.linear_model import LogisticRegression -from sklearn.linear_model import RidgeCV -from sklearn.metrics import accuracy_score -from sklearn.metrics import auc -from sklearn.metrics import average_precision_score -from sklearn.metrics import classification_report -from sklearn.metrics import confusion_matrix -from sklearn.metrics import explained_variance_score -from sklearn.metrics import f1_score -from sklearn.metrics import log_loss -from sklearn.metrics import mean_absolute_error -from sklearn.metrics import mean_squared_error -from sklearn.metrics import median_absolute_error -from sklearn.metrics import precision_score -from sklearn.metrics import r2_score -from sklearn.metrics import recall_score -from sklearn.metrics import roc_auc_score -from sklearn.metrics import roc_curve -from sklearn.metrics.cluster import adjusted_rand_score -from sklearn.model_selection import train_test_split -import sys -import yaml - - -# -# Initialize logger -# - -logger = logging.getLogger(__name__) - - -# -# Class Model -# -# model unifies algorithms and we use hasattr to list the available attrs for each -# algorithm so users can query an algorithm and get the list of attributes -# - -class Model: - """Create a new model. - - Parameters - ---------- - specs : dict - The model specifications obtained by reading the ``model.yml`` - file. - - Attributes - ---------- - specs : dict - The model specifications. - X_train : pandas.DataFrame - Training features in matrix format. - X_test : pandas.Series - Testing features in matrix format. - y_train : pandas.DataFrame - Training labels in vector format. - y_test : pandas.Series - Testing labels in vector format. - algolist : list - Algorithms to use in training. - estimators : dict - Dictionary of estimators (key: algorithm) - importances : dict - Feature Importances (key: algorithm) - coefs : dict - Coefficients, if applicable (key: algorithm) - support : dict - Support Vectors, if applicable (key: algorithm) - preds : dict - Predictions or labels (keys: algorithm, partition) - probas : dict - Probabilities from classification (keys: algorithm, partition) - metrics : dict - Model evaluation metrics (keys: algorith, partition, metric) - - Raises - ------ - KeyError - Model specs must include the key *algorithms*, which is - stored in ``algolist``. - - """ - - # __init__ - - def __init__(self, - specs): - # specifications - self.specs = specs - # data in memory - self.X_train = None - self.X_test = None - self.y_train = None - self.y_test = None - # test labels - self.test_labels = False - # datasets - self.train_file = datasets[Partition.train] - self.test_file = datasets[Partition.test] - self.predict_file = datasets[Partition.predict] - # algorithms - try: - self.algolist = self.specs['algorithms'] - except: - raise KeyError("Model specs must include the key: algorithms") - # feature map - self.feature_map = {} - # Key: (algorithm) - self.estimators = {} - self.importances = {} - self.coefs = {} - self.support = {} - # Keys: (algorithm, partition) - self.preds = {} - self.probas = {} - # Keys: (algorithm, partition, metric) - self.metrics = {} - - # __str__ - - def __str__(self): - return self.name - - # __getnewargs__ - - def __getnewargs__(self): - return (self.specs,) - - -# -# Function get_model_config -# - -def get_model_config(): - r"""Read in the configuration file for AlphaPy. - - Parameters - ---------- - None : None - - Returns - ------- - specs : dict - The parameters for controlling AlphaPy. - - Raises - ------ - ValueError - Unrecognized value of a ``model.yml`` field. - - """ - - logger.info("Model Configuration") - - # Read the configuration file - - full_path = SSEP.join([PSEP, 'config', 'model.yml']) - with open(full_path, 'r') as ymlfile: - cfg = yaml.load(ymlfile) - - # Store configuration parameters in dictionary - - specs = {} - - # Section: project [this section must be first] - - specs['directory'] = cfg['project']['directory'] - specs['extension'] = cfg['project']['file_extension'] - specs['submission_file'] = cfg['project']['submission_file'] - specs['submit_probas'] = cfg['project']['submit_probas'] - - # Section: data - - specs['drop'] = cfg['data']['drop'] - specs['features'] = cfg['data']['features'] - specs['sentinel'] = cfg['data']['sentinel'] - specs['separator'] = cfg['data']['separator'] - specs['shuffle'] = cfg['data']['shuffle'] - specs['split'] = cfg['data']['split'] - specs['target'] = cfg['data']['target'] - specs['target_value'] = cfg['data']['target_value'] - # sampling - specs['sampling'] = cfg['data']['sampling']['option'] - # determine whether or not sampling method is valid - samplers = {x.name: x.value for x in SamplingMethod} - sampling_method = cfg['data']['sampling']['method'] - if sampling_method in samplers: - specs['sampling_method'] = SamplingMethod(samplers[sampling_method]) - else: - raise ValueError("model.yml data:sampling:method %s unrecognized" % - sampling_method) - # end of sampling method - specs['sampling_ratio'] = cfg['data']['sampling']['ratio'] - - # Section: features - - # clustering - specs['clustering'] = cfg['features']['clustering']['option'] - specs['cluster_min'] = cfg['features']['clustering']['minimum'] - specs['cluster_max'] = cfg['features']['clustering']['maximum'] - specs['cluster_inc'] = cfg['features']['clustering']['increment'] - # counts - specs['counts'] = cfg['features']['counts']['option'] - # encoding - specs['rounding'] = cfg['features']['encoding']['rounding'] - # determine whether or not encoder is valid - encoders = {x.name: x.value for x in Encoders} - encoder = cfg['features']['encoding']['type'] - if encoder in encoders: - specs['encoder'] = Encoders(encoders[encoder]) - else: - raise ValueError("model.yml features:encoding:type %s unrecognized" % encoder) - # factors - specs['factors'] = cfg['features']['factors'] - # interactions - specs['interactions'] = cfg['features']['interactions']['option'] - specs['isample_pct'] = cfg['features']['interactions']['sampling_pct'] - specs['poly_degree'] = cfg['features']['interactions']['poly_degree'] - # isomap - specs['isomap'] = cfg['features']['isomap']['option'] - specs['iso_components'] = cfg['features']['isomap']['components'] - specs['iso_neighbors'] = cfg['features']['isomap']['neighbors'] - # log transformation - specs['logtransform'] = cfg['features']['logtransform']['option'] - # low-variance features - specs['lv_remove'] = cfg['features']['variance']['option'] - specs['lv_threshold'] = cfg['features']['variance']['threshold'] - # NumPy - specs['numpy'] = cfg['features']['numpy']['option'] - # pca - specs['pca'] = cfg['features']['pca']['option'] - specs['pca_min'] = cfg['features']['pca']['minimum'] - specs['pca_max'] = cfg['features']['pca']['maximum'] - specs['pca_inc'] = cfg['features']['pca']['increment'] - specs['pca_whiten'] = cfg['features']['pca']['whiten'] - # Scaling - specs['scaler_option'] = cfg['features']['scaling']['option'] - # determine whether or not scaling type is valid - scaler_types = {x.name: x.value for x in Scalers} - scaler_type = cfg['features']['scaling']['type'] - if scaler_type in scaler_types: - specs['scaler_type'] = Scalers(scaler_types[scaler_type]) - else: - raise ValueError("model.yml features:scaling:type %s unrecognized" % scaler_type) - # SciPy - specs['scipy'] = cfg['features']['scipy']['option'] - # text - specs['ngrams_max'] = cfg['features']['text']['ngrams'] - specs['vectorize'] = cfg['features']['text']['vectorize'] - # t-sne - specs['tsne'] = cfg['features']['tsne']['option'] - specs['tsne_components'] = cfg['features']['tsne']['components'] - specs['tsne_learn_rate'] = cfg['features']['tsne']['learning_rate'] - specs['tsne_perplexity'] = cfg['features']['tsne']['perplexity'] - - # Section: model - - specs['algorithms'] = cfg['model']['algorithms'] - specs['balance_classes'] = cfg['model']['balance_classes'] - specs['cv_folds'] = cfg['model']['cv_folds'] - # determine whether or not model type is valid - model_types = {x.name: x.value for x in ModelType} - model_type = cfg['model']['type'] - if model_type in model_types: - specs['model_type'] = ModelType(model_types[model_type]) - else: - raise ValueError("model.yml model:type %s unrecognized" % model_type) - # end of model type - specs['n_estimators'] = cfg['model']['estimators'] - specs['pvalue_level'] = cfg['model']['pvalue_level'] - specs['scorer'] = cfg['model']['scoring_function'] - # calibration - specs['calibration'] = cfg['model']['calibration']['option'] - specs['cal_type'] = cfg['model']['calibration']['type'] - # feature selection - specs['feature_selection'] = cfg['model']['feature_selection']['option'] - specs['fs_percentage'] = cfg['model']['feature_selection']['percentage'] - specs['fs_uni_grid'] = cfg['model']['feature_selection']['uni_grid'] - score_func = cfg['model']['feature_selection']['score_func'] - if score_func in feature_scorers: - specs['fs_score_func'] = feature_scorers[score_func] - else: - raise ValueError("model.yml model:feature_selection:score_func %s unrecognized" % - score_func) - # grid search - specs['grid_search'] = cfg['model']['grid_search']['option'] - specs['gs_iters'] = cfg['model']['grid_search']['iterations'] - specs['gs_random'] = cfg['model']['grid_search']['random'] - specs['gs_sample'] = cfg['model']['grid_search']['subsample'] - specs['gs_sample_pct'] = cfg['model']['grid_search']['sampling_pct'] - # rfe - specs['rfe'] = cfg['model']['rfe']['option'] - specs['rfe_step'] = cfg['model']['rfe']['step'] - - # Section: pipeline - - specs['n_jobs'] = cfg['pipeline']['number_jobs'] - specs['seed'] = cfg['pipeline']['seed'] - specs['verbosity'] = cfg['pipeline']['verbosity'] - - # Section: plots - - specs['calibration_plot'] = cfg['plots']['calibration'] - specs['confusion_matrix'] = cfg['plots']['confusion_matrix'] - specs['importances'] = cfg['plots']['importances'] - specs['learning_curve'] = cfg['plots']['learning_curve'] - specs['roc_curve'] = cfg['plots']['roc_curve'] - - # Section: treatments - - try: - specs['treatments'] = cfg['treatments'] - except: - specs['treatments'] = None - logger.info("No Treatments Found") - - # Section: xgboost - - specs['esr'] = cfg['xgboost']['stopping_rounds'] - - # Log the configuration parameters - - logger.info('MODEL PARAMETERS:') - logger.info('algorithms = %s', specs['algorithms']) - logger.info('balance_classes = %s', specs['balance_classes']) - logger.info('calibration = %r', specs['calibration']) - logger.info('cal_type = %s', specs['cal_type']) - logger.info('calibration_plot = %r', specs['calibration']) - logger.info('clustering = %r', specs['clustering']) - logger.info('cluster_inc = %d', specs['cluster_inc']) - logger.info('cluster_max = %d', specs['cluster_max']) - logger.info('cluster_min = %d', specs['cluster_min']) - logger.info('confusion_matrix = %r', specs['confusion_matrix']) - logger.info('counts = %r', specs['counts']) - logger.info('cv_folds = %d', specs['cv_folds']) - logger.info('directory = %s', specs['directory']) - logger.info('extension = %s', specs['extension']) - logger.info('drop = %s', specs['drop']) - logger.info('encoder = %r', specs['encoder']) - logger.info('esr = %d', specs['esr']) - logger.info('factors = %s', specs['factors']) - logger.info('features [X] = %s', specs['features']) - logger.info('feature_selection = %r', specs['feature_selection']) - logger.info('fs_percentage = %d', specs['fs_percentage']) - logger.info('fs_score_func = %s', specs['fs_score_func']) - logger.info('fs_uni_grid = %s', specs['fs_uni_grid']) - logger.info('grid_search = %r', specs['grid_search']) - logger.info('gs_iters = %d', specs['gs_iters']) - logger.info('gs_random = %r', specs['gs_random']) - logger.info('gs_sample = %r', specs['gs_sample']) - logger.info('gs_sample_pct = %f', specs['gs_sample_pct']) - logger.info('importances = %r', specs['importances']) - logger.info('interactions = %r', specs['interactions']) - logger.info('isomap = %r', specs['isomap']) - logger.info('iso_components = %d', specs['iso_components']) - logger.info('iso_neighbors = %d', specs['iso_neighbors']) - logger.info('isample_pct = %d', specs['isample_pct']) - logger.info('learning_curve = %r', specs['learning_curve']) - logger.info('logtransform = %r', specs['logtransform']) - logger.info('lv_remove = %r', specs['lv_remove']) - logger.info('lv_threshold = %f', specs['lv_threshold']) - logger.info('model_type = %r', specs['model_type']) - logger.info('n_estimators = %d', specs['n_estimators']) - logger.info('n_jobs = %d', specs['n_jobs']) - logger.info('ngrams_max = %d', specs['ngrams_max']) - logger.info('numpy = %r', specs['numpy']) - logger.info('pca = %r', specs['pca']) - logger.info('pca_inc = %d', specs['pca_inc']) - logger.info('pca_max = %d', specs['pca_max']) - logger.info('pca_min = %d', specs['pca_min']) - logger.info('pca_whiten = %r', specs['pca_whiten']) - logger.info('poly_degree = %d', specs['poly_degree']) - logger.info('pvalue_level = %f', specs['pvalue_level']) - logger.info('rfe = %r', specs['rfe']) - logger.info('rfe_step = %d', specs['rfe_step']) - logger.info('roc_curve = %r', specs['roc_curve']) - logger.info('rounding = %d', specs['rounding']) - logger.info('sampling = %r', specs['sampling']) - logger.info('sampling_method = %r', specs['sampling_method']) - logger.info('sampling_ratio = %f', specs['sampling_ratio']) - logger.info('scaler_option = %r', specs['scaler_option']) - logger.info('scaler_type = %r', specs['scaler_type']) - logger.info('scipy = %r', specs['scipy']) - logger.info('scorer = %s', specs['scorer']) - logger.info('seed = %d', specs['seed']) - logger.info('sentinel = %d', specs['sentinel']) - logger.info('separator = %s', specs['separator']) - logger.info('shuffle = %r', specs['shuffle']) - logger.info('split = %f', specs['split']) - logger.info('submission_file = %s', specs['submission_file']) - logger.info('submit_probas = %r', specs['submit_probas']) - logger.info('target [y] = %s', specs['target']) - logger.info('target_value = %d', specs['target_value']) - logger.info('treatments = %s', specs['treatments']) - logger.info('tsne = %r', specs['tsne']) - logger.info('tsne_components = %d', specs['tsne_components']) - logger.info('tsne_learn_rate = %f', specs['tsne_learn_rate']) - logger.info('tsne_perplexity = %f', specs['tsne_perplexity']) - logger.info('vectorize = %r', specs['vectorize']) - logger.info('verbosity = %d', specs['verbosity']) - - # Specifications to create the model - return specs - - -# -# Function load_predictor -# - -def load_predictor(directory): - r"""Load the model predictor from storage. By default, the - most recent model is loaded into memory. - - Parameters - ---------- - directory : str - Full directory specification of the predictor's location. - - Returns - ------- - predictor : function - The scoring function. - - """ - - # Create search path - search_path = SSEP.join([directory, 'model', 'model_*.pkl']) - - # Locate the model Pickle file - - try: - # find the latest file - filename = max(glob.iglob(search_path), key=os.path.getctime) - logger.info("Loading model predictor from %s", filename) - # load the model predictor - predictor = joblib.load(filename) - except: - logging.error("Could not find model predictor in %s", search_path) - - # Return the model predictor - return predictor - - -# -# Function save_predictor -# - -def save_predictor(model, timestamp): - r"""Save the time-stamped model predictor to disk. - - Parameters - ---------- - model : alphapy.Model - The model object that contains the best estimator. - timestamp : str - Date in yyyy-mm-dd format. - - Returns - ------- - None : None - - """ - - logger.info("Saving Model Predictor") - - # Extract model parameters. - directory = model.specs['directory'] - - # Get the best predictor - predictor = model.estimators['BEST'] - - # Create full path name. - - filename = 'model_' + timestamp + '.pkl' - full_path = SSEP.join([directory, 'model', filename]) - - # Save model object - - logger.info("Writing model predictor to %s", full_path) - joblib.dump(predictor, full_path) - - -# -# Function load_feature_map -# - -def load_feature_map(model, directory): - r"""Load the feature map from storage. By default, the - most recent feature map is loaded into memory. - - Parameters - ---------- - model : alphapy.Model - The model object to contain the feature map. - directory : str - Full directory specification of the feature map's location. - - Returns - ------- - model : alphapy.Model - The model object containing the feature map. - - """ - - # Create search path - search_path = SSEP.join([directory, 'model', 'feature_map_*.pkl']) - - # Locate the feature map and load it - - try: - # find the latest file - filename = max(glob.iglob(search_path), key=os.path.getctime) - logger.info("Loading feature map from %s", filename) - # load the feature map - feature_map = joblib.load(filename) - model.feature_map = feature_map - except: - logging.error("Could not find feature map in %s", search_path) - - # Return the model with the feature map - return model - - -# -# Function save_feature_map -# - -def save_feature_map(model, timestamp): - r"""Save the feature map to disk. - - Parameters - ---------- - model : alphapy.Model - The model object containing the feature map. - timestamp : str - Date in yyyy-mm-dd format. - - Returns - ------- - None : None - - """ - - logger.info("Saving Feature Map") - - # Extract model parameters. - directory = model.specs['directory'] - - # Create full path name. - - filename = 'feature_map_' + timestamp + '.pkl' - full_path = SSEP.join([directory, 'model', filename]) - - # Save model object - - logger.info("Writing feature map to %s", full_path) - joblib.dump(model.feature_map, full_path) - - -# -# Function get_class_weights -# - -def get_class_weights(model): - r"""Set the class weights for fitting the model. - - Parameters - ---------- - model : alphapy.Model - The model object with specifications. - - Returns - ------- - model : alphapy.Model - The model object with class weights. - - """ - - # Extract model parameters. - - balance_classes = model.specs['balance_classes'] - target = model.specs['target'] - target_value = model.specs['target_value'] - - # Extract model data. - - y_train = model.y_train - - # Calculate sample weights - - sw = None - if balance_classes: - logger.info("Getting Class Weights") - uv, uc = np.unique(y_train, return_counts=True) - target_index = np.where(uv == target_value)[0][0] - nontarget_index = np.where(uv != target_value)[0][0] - weight = uc[nontarget_index] / uc[target_index] - logger.info("Class Weight for target %s [%r]: %f", - target, target_value, weight) - sw = [weight if x==target_value else 1.0 for x in y_train] - else: - logger.info("Skipping Class Weights") - - # Set weights - - model.specs['class_weights'] = sw - return model - - -# -# Function first_fit -# - -def first_fit(model, algo, est): - r"""Fit the model before optimization. - - Parameters - ---------- - model : alphapy.Model - The model object with specifications. - algo : str - Abbreviation of the algorithm to run. - est : alphapy.Estimator - The estimator to fit. - - Returns - ------- - model : alphapy.Model - The model object with the initial estimator. - - Notes - ----- - AlphaPy fits an initial model because the user may choose to get - a first score without any additional feature selection or grid - search. XGBoost is a special case because it has the advantage - of an ``eval_set`` and ``early_stopping_rounds``, which can - speed up the estimation phase. - - """ - - logger.info("Fitting Initial Model") - - # Extract model parameters. - - esr = model.specs['esr'] - model_type = model.specs['model_type'] - scorer = model.specs['scorer'] - seed = model.specs['seed'] - split = model.specs['split'] - - # Initialize class weights. - - if model_type == ModelType.classification: - class_weights = model.specs['class_weights'] - else: - class_weights = None - - # Extract model data. - - X_train = model.X_train - y_train = model.y_train - - # Fit the initial model. - - if 'XGB' in algo and scorer in xgb_score_map: - X1, X2, y1, y2 = train_test_split(X_train, y_train, test_size=split, - random_state=seed) - eval_set = [(X1, y1), (X2, y2)] - eval_metric = xgb_score_map[scorer] - est.fit(X1, y1, eval_set=eval_set, eval_metric=eval_metric, - early_stopping_rounds=esr) - elif class_weights and model_type != ModelType.classification: - est.fit(X_train, y_train, sample_weight=class_weights) - else: - est.fit(X_train, y_train) - - # Store the estimator - - model.estimators[algo] = est - - # Record importances and coefficients if necessary. - - if hasattr(est, "feature_importances_"): - model.importances[algo] = est.feature_importances_ - - if hasattr(est, "coef_"): - model.coefs[algo] = est.coef_ - - # Save the estimator in the model and return the model - return model - - -# -# Function make_predictions -# - -def make_predictions(model, algo, calibrate): - r"""Make predictions for the training and testing data. - - Parameters - ---------- - model : alphapy.Model - The model object with specifications. - algo : str - Abbreviation of the algorithm to make predictions. - calibrate : bool - If ``True``, calibrate the probabilities for a classifier. - - Returns - ------- - model : alphapy.Model - The model object with the predictions. - - Notes - ----- - For classification, calibration is a precursor to making the - actual predictions. In this case, AlphaPy predicts both labels - and probabilities. For regression, real values are predicted. - - """ - - logger.info("Final Model Predictions for %s", algo) - - # Extract model parameters. - - cal_type = model.specs['cal_type'] - cv_folds = model.specs['cv_folds'] - model_type = model.specs['model_type'] - - # Initialize class weights. - - if model_type == ModelType.classification: - class_weights = model.specs['class_weights'] - else: - class_weights = None - - # Get the estimator - - est = model.estimators[algo] - - # Extract model data. - - try: - support = model.support[algo] - X_train = model.X_train[:, support] - X_test = model.X_test[:, support] - except: - X_train = model.X_train - X_test = model.X_test - y_train = model.y_train - - # Calibration - - if model_type == ModelType.classification: - if calibrate: - logger.info("Calibrating Classifier") - est = CalibratedClassifierCV(est, cv=cv_folds, method=cal_type) - est.fit(X_train, y_train, sample_weight=class_weights) - model.estimators[algo] = est - logger.info("Calibration Complete") - else: - logger.info("Skipping Calibration") - - # Make predictions on original training and test data. - - logger.info("Making Predictions") - model.preds[(algo, Partition.train)] = est.predict(X_train) - model.preds[(algo, Partition.test)] = est.predict(X_test) - if model_type == ModelType.classification: - model.probas[(algo, Partition.train)] = est.predict_proba(X_train)[:, 1] - model.probas[(algo, Partition.test)] = est.predict_proba(X_test)[:, 1] - logger.info("Predictions Complete") - - # Return the model - return model - - -# -# Function predict_best -# - -def predict_best(model): - r"""Select the best model based on score. - - Parameters - ---------- - model : alphapy.Model - The model object with all of the estimators. - - Returns - ------- - model : alphapy.Model - The model object with the best estimator. - - Notes - ----- - Best model selection is based on a scoring function. If the - objective is to minimize (e.g., negative log loss), then we - select the model with the algorithm that has the lowest score. - If the objective is to maximize, then we select the algorithm - with the highest score (e.g., AUC). - - For multiple algorithms, AlphaPy always creates a blended model. - Therefore, the best algorithm that is selected could actually - be the blended model itself. - - """ - - logger.info('='*80) - logger.info("Selecting Best Model") - - # Define model tags - - best_tag = 'BEST' - blend_tag = 'BLEND' - - # Extract model parameters. - - model_type = model.specs['model_type'] - rfe = model.specs['rfe'] - scorer = model.specs['scorer'] - test_labels = model.test_labels - - # Determine the correct partition to select the best model - - partition = Partition.test if test_labels else Partition.train - logger.info("Scoring for: %s", partition) - - # Initialize best parameters. - - maximize = True if scorers[scorer][1] == Objective.maximize else False - if maximize: - best_score = -sys.float_info.max - else: - best_score = sys.float_info.max - - # Initialize the model selection process. - - start_time = datetime.now() - logger.info("Best Model Selection Start: %s", start_time) - - # Add blended model to the list of algorithms. - - if len(model.algolist) > 1: - algolist = copy(model.algolist) - algolist.append(blend_tag) - else: - algolist = model.algolist - - # Iterate through the models, getting the best score for each one. - - for algorithm in algolist: - logger.info("Scoring %s Model", algorithm) - top_score = model.metrics[(algorithm, partition, scorer)] - # objective is to either maximize or minimize score - if maximize: - if top_score > best_score: - best_score = top_score - best_algo = algorithm - else: - if top_score < best_score: - best_score = top_score - best_algo = algorithm - - # Record predictions of best estimator - - logger.info("Best Model is %s with a %s score of %.4f", best_algo, scorer, best_score) - model.estimators[best_tag] = model.estimators[best_algo] - model.preds[(best_tag, Partition.train)] = model.preds[(best_algo, Partition.train)] - model.preds[(best_tag, Partition.test)] = model.preds[(best_algo, Partition.test)] - if model_type == ModelType.classification: - model.probas[(best_tag, Partition.train)] = model.probas[(best_algo, Partition.train)] - model.probas[(best_tag, Partition.test)] = model.probas[(best_algo, Partition.test)] - - # Record support vector for any recursive feature elimination - - if rfe and 'XGB' not in best_algo: - try: - model.feature_map['rfe_support'] = model.support[best_algo] - except: - # no RFE support for best algorithm - pass - - # Return the model with best estimator and predictions. - - end_time = datetime.now() - time_taken = end_time - start_time - logger.info("Best Model Selection Complete: %s", time_taken) - - return model - - -# -# Function predict_blend -# - -def predict_blend(model): - r"""Make predictions from a blended model. - - Parameters - ---------- - model : alphapy.Model - The model object with all of the estimators. - - Returns - ------- - model : alphapy.Model - The model object with the blended estimator. - - Notes - ----- - For classification, AlphaPy uses logistic regression for creating - a blended model. For regression, ridge regression is applied. - - """ - - logger.info("Blending Models") - - # Extract model paramters. - - model_type = model.specs['model_type'] - cv_folds = model.specs['cv_folds'] - - # Extract model data. - - X_train = model.X_train - X_test = model.X_test - y_train = model.y_train - - # Add blended algorithm. - - blend_tag = 'BLEND' - - # Create blended training and test sets. - - n_models = len(model.algolist) - X_blend_train = np.zeros((X_train.shape[0], n_models)) - X_blend_test = np.zeros((X_test.shape[0], n_models)) - - # Iterate through the models, cross-validating for each one. - - start_time = datetime.now() - logger.info("Blending Start: %s", start_time) - - for i, algorithm in enumerate(model.algolist): - # get the best estimator - estimator = model.estimators[algorithm] - # update coefficients and feature importances - if hasattr(estimator, "coef_"): - model.coefs[algorithm] = estimator.coef_ - if hasattr(estimator, "feature_importances_"): - model.importances[algorithm] = estimator.feature_importances_ - # store predictions in the blended training set - if model_type == ModelType.classification: - X_blend_train[:, i] = model.probas[(algorithm, Partition.train)] - X_blend_test[:, i] = model.probas[(algorithm, Partition.test)] - else: - X_blend_train[:, i] = model.preds[(algorithm, Partition.train)] - X_blend_test[:, i] = model.preds[(algorithm, Partition.test)] - - # Use the blended estimator to make predictions - - if model_type == ModelType.classification: - clf = LogisticRegression() - clf.fit(X_blend_train, y_train) - model.estimators[blend_tag] = clf - model.preds[(blend_tag, Partition.train)] = clf.predict(X_blend_train) - model.preds[(blend_tag, Partition.test)] = clf.predict(X_blend_test) - model.probas[(blend_tag, Partition.train)] = clf.predict_proba(X_blend_train)[:, 1] - model.probas[(blend_tag, Partition.test)] = clf.predict_proba(X_blend_test)[:, 1] - else: - alphas = [0.0001, 0.005, 0.001, 0.005, 0.01, 0.05, 0.1, 0.5, - 1.0, 5.0, 10.0, 50.0, 100.0, 500.0, 1000.0] - rcvr = RidgeCV(alphas=alphas, normalize=True, cv=cv_folds) - rcvr.fit(X_blend_train, y_train) - model.estimators[blend_tag] = rcvr - model.preds[(blend_tag, Partition.train)] = rcvr.predict(X_blend_train) - model.preds[(blend_tag, Partition.test)] = rcvr.predict(X_blend_test) - - # Return the model with blended estimator and predictions. - - end_time = datetime.now() - time_taken = end_time - start_time - logger.info("Blending Complete: %s", time_taken) - - return model - - -# -# Function generate_metrics -# - -def generate_metrics(model, partition): - r"""Generate model evaluation metrics for all estimators. - - Parameters - ---------- - model : alphapy.Model - The model object with stored predictions. - partition : alphapy.Partition - Reference to the dataset. - - Returns - ------- - model : alphapy.Model - The model object with the completed metrics. - - Notes - ----- - AlphaPy takes a brute-force approach to calculating each metric. - It calls every scikit-learn function without exception. If the - calculation fails for any reason, then the evaluation will still - continue without error. - - References - ---------- - For more information about model evaluation and the associated metrics, - refer to [EVAL]_. - - .. [EVAL] http://scikit-learn.org/stable/modules/model_evaluation.html - - """ - - logger.info('='*80) - logger.info("Metrics for: %s", partition) - - # Extract model paramters. - - model_type = model.specs['model_type'] - - # Extract model data. - - if partition == Partition.train: - expected = model.y_train - else: - expected = model.y_test - - # Generate Metrics - - if expected.any(): - # Add blended model to the list of algorithms. - if len(model.algolist) > 1: - algolist = copy(model.algolist) - algolist.append('BLEND') - else: - algolist = model.algolist - - # get the metrics for each algorithm - for algo in algolist: - # get predictions for the given algorithm - predicted = model.preds[(algo, partition)] - try: - model.metrics[(algo, partition, 'accuracy')] = accuracy_score(expected, predicted) - except: - logger.info("Accuracy Score not calculated") - try: - model.metrics[(algo, partition, 'adjusted_rand_score')] = adjusted_rand_score(expected, predicted) - except: - logger.info("Adjusted Rand Index not calculated") - try: - model.metrics[(algo, partition, 'confusion_matrix')] = confusion_matrix(expected, predicted) - except: - logger.info("Confusion Matrix not calculated") - try: - model.metrics[(algo, partition, 'explained_variance')] = explained_variance_score(expected, predicted) - except: - logger.info("Explained Variance Score not calculated") - try: - model.metrics[(algo, partition, 'f1')] = f1_score(expected, predicted) - except: - logger.info("F1 Score not calculated") - try: - model.metrics[(algo, partition, 'mean_absolute_error')] = mean_absolute_error(expected, predicted) - except: - logger.info("Mean Absolute Error not calculated") - try: - model.metrics[(algo, partition, 'median_absolute_error')] = median_absolute_error(expected, predicted) - except: - logger.info("Median Absolute Error not calculated") - try: - model.metrics[(algo, partition, 'neg_mean_squared_error')] = mean_squared_error(expected, predicted) - except: - logger.info("Mean Squared Error not calculated") - try: - model.metrics[(algo, partition, 'precision')] = precision_score(expected, predicted) - except: - logger.info("Precision Score not calculated") - try: - model.metrics[(algo, partition, 'r2')] = r2_score(expected, predicted) - except: - logger.info("R-Squared Score not calculated") - try: - model.metrics[(algo, partition, 'recall')] = recall_score(expected, predicted) - except: - logger.info("Recall Score not calculated") - # Probability-Based Metrics - if model_type == ModelType.classification: - predicted = model.probas[(algo, partition)] - try: - model.metrics[(algo, partition, 'average_precision')] = average_precision_score(expected, predicted) - except: - logger.info("Average Precision Score not calculated") - try: - model.metrics[(algo, partition, 'neg_log_loss')] = log_loss(expected, predicted) - except: - logger.info("Log Loss not calculated") - try: - fpr, tpr, _ = roc_curve(expected, predicted) - model.metrics[(algo, partition, 'roc_auc')] = auc(fpr, tpr) - except: - logger.info("ROC AUC Score not calculated") - # log the metrics for each algorithm - for algo in model.algolist: - logger.info('-'*80) - logger.info("Algorithm: %s", algo) - metrics = [(k[2], v) for k, v in model.metrics.items() if k[0] == algo and k[1] == partition] - for key, value in sorted(metrics): - svalue = str(value) - svalue.replace('\n', ' ') - logger.info("%s: %s", key, svalue) - else: - logger.info("No labels for generating %s metrics", partition) - - return model - - -# -# Function save_predictions -# - -def save_predictions(model, tag, partition): - r"""Save the predictions to disk. - - Parameters - ---------- - model : alphapy.Model - The model object to save. - tag : str - A unique identifier for the output files, e.g., a date stamp. - partition : alphapy.Partition - Reference to the dataset. - - Returns - ------- - preds : numpy array - The prediction vector. - probas : numpy array - The probability vector. - - """ - - # Extract model parameters. - - directory = model.specs['directory'] - extension = model.specs['extension'] - model_type = model.specs['model_type'] - separator = model.specs['separator'] - - # Get date stamp to record file creation - - d = datetime.now() - f = "%Y%m%d" - timestamp = d.strftime(f) - - # Specify input and output directories - - input_dir = SSEP.join([directory, 'input']) - output_dir = SSEP.join([directory, 'output']) - - # Read the prediction frame - pf = read_frame(input_dir, datasets[partition], extension, separator) - - # Cull records before the prediction date - - try: - predict_date = model.specs['predict_date'] - found_pdate = True - except: - found_pdate = False - - if found_pdate: - pd_indices = pf[pf.date >= predict_date].index.tolist() - pf = pf.ix[pd_indices] - - # Save predictions for all projects - - logger.info("Saving Predictions") - output_file = USEP.join(['predictions', timestamp]) - preds = model.preds[(tag, partition)] - if found_pdate: - preds = np.take(preds, pd_indices) - np_store_data(preds, output_dir, output_file, extension, separator) - - # Save probabilities for classification projects - - probas = None - if model_type == ModelType.classification: - logger.info("Saving Probabilities") - output_file = USEP.join(['probabilities', timestamp]) - probas = model.probas[(tag, partition)] - if found_pdate: - probas = np.take(probas, pd_indices) - np_store_data(probas, output_dir, output_file, extension, separator) - - # Save ranked predictions - - logger.info("Saving Ranked Predictions") - pf['prediction'] = pd.Series(preds, index=pf.index) - if model_type == ModelType.classification: - pf['probability'] = pd.Series(probas, index=pf.index) - pf.sort_values('probability', ascending=False, inplace=True) - else: - pf.sort_values('prediction', ascending=False, inplace=True) - output_file = USEP.join(['rankings', timestamp]) - write_frame(pf, output_dir, output_file, extension, separator) - - # Return predictions and any probabilities - return preds, probas - - -# -# Function save_model -# - -def save_model(model, tag, partition): - r"""Save the results in the model file. - - Parameters - ---------- - model : alphapy.Model - The model object to save. - tag : str - A unique identifier for the output files, e.g., a date stamp. - partition : alphapy.Partition - Reference to the dataset. - - Returns - ------- - None : None - - Notes - ----- - - The following components are extracted from the model object - and saved to disk: - - * Model predictor (via joblib/pickle) - * Predictions - * Probabilities (classification only) - * Rankings - * Submission File (optional) - - """ - - logger.info('='*80) - - # Extract model parameters. - - directory = model.specs['directory'] - extension = model.specs['extension'] - model_type = model.specs['model_type'] - submission_file = model.specs['submission_file'] - submit_probas = model.specs['submit_probas'] - - # Get date stamp to record file creation - - d = datetime.now() - f = "%Y%m%d" - timestamp = d.strftime(f) - - # Save the model predictor - save_predictor(model, timestamp) - - # Save the feature map - save_feature_map(model, timestamp) - - # Specify input and output directories - - input_dir = SSEP.join([directory, 'input']) - output_dir = SSEP.join([directory, 'output']) - - # Save predictions - preds, probas = save_predictions(model, tag, partition) - - # Generate submission file - - if submission_file: - sample_spec = PSEP.join([submission_file, extension]) - sample_input = SSEP.join([input_dir, sample_spec]) - ss = pd.read_csv(sample_input) - if submit_probas and model_type == ModelType.classification: - ss[ss.columns[1]] = probas - else: - ss[ss.columns[1]] = preds - submission_base = USEP.join(['submission', timestamp]) - submission_spec = PSEP.join([submission_base, extension]) - submission_output = SSEP.join([output_dir, submission_spec]) - logger.info("Saving Submission to %s", submission_output) - ss.to_csv(submission_output, index=False) diff --git a/alphapy/optimize.py.bak b/alphapy/optimize.py.bak deleted file mode 100644 index 47c1b17..0000000 --- a/alphapy/optimize.py.bak +++ /dev/null @@ -1,363 +0,0 @@ -################################################################################ -# -# Package : AlphaPy -# Module : optimize -# Created : July 11, 2013 -# -# Copyright 2017 ScottFree Analytics LLC -# Mark Conway & Robert D. Scott II -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. -# -################################################################################ - - -# -# Imports -# - -from alphapy.globals import ModelType - -from datetime import datetime -import logging -import numpy as np -from sklearn.feature_selection import RFE -from sklearn.feature_selection import RFECV -from sklearn.feature_selection import SelectPercentile -from sklearn.model_selection import GridSearchCV -from sklearn.model_selection import RandomizedSearchCV -from sklearn.pipeline import Pipeline -from time import time - - -# -# Initialize logger -# - -logger = logging.getLogger(__name__) - - -# -# Function rfecv_search -# - -def rfecv_search(model, algo): - r"""Return the best feature set using recursive feature elimination - with cross-validation. - - Parameters - ---------- - model : alphapy.Model - The model object with RFE parameters. - algo : str - Abbreviation of the algorithm to run. - - Returns - ------- - model : alphapy.Model - The model object with the RFE support vector and the best - estimator. - - See Also - -------- - rfe_search - - Notes - ----- - If a scoring function is available, then AlphaPy can perform RFE - with Cross-Validation (CV), as in this function; otherwise, it just - does RFE without CV. - - References - ---------- - For more information about Recursive Feature Elimination, - refer to [RFECV]_. - - .. [RFECV] http://scikit-learn.org/stable/modules/generated/sklearn.feature_selection.RFECV.html - - """ - - # Extract model data. - - X_train = model.X_train - y_train = model.y_train - - # Extract model parameters. - - cv_folds = model.specs['cv_folds'] - rfe_step = model.specs['rfe_step'] - scorer = model.specs['scorer'] - verbosity = model.specs['verbosity'] - estimator = model.estimators[algo] - - # Perform Recursive Feature Elimination - - logger.info("Recursive Feature Elimination with CV") - rfecv = RFECV(estimator, step=rfe_step, cv=cv_folds, - scoring=scorer, verbose=verbosity) - start = time() - selector = rfecv.fit(X_train, y_train) - logger.info("RFECV took %.2f seconds for step %d and %d folds", - (time() - start), rfe_step, cv_folds) - logger.info("Algorithm: %s, Selected Features: %d, Ranking: %s", - algo, selector.n_features_, selector.ranking_) - - # Record the new estimator and support vector - - model.estimators[algo] = selector.estimator_ - model.support[algo] = selector.support_ - - # Return the model with the support vector - - return model - - -# -# Function rfe_search -# - -def rfe_search(model, algo): - r"""Return the best feature set using recursive feature elimination. - - Parameters - ---------- - model : alphapy.Model - The model object with RFE parameters. - algo : str - Abbreviation of the algorithm to run. - - Returns - ------- - model : alphapy.Model - The model object with the RFE support vector and the best - estimator. - - See Also - -------- - rfecv_search - - Notes - ----- - If a scoring function is available, then AlphaPy can perform RFE - with Cross-Validation (CV); otherwise, it just does RFE without CV, - as in this function. - - References - ---------- - For more information about Recursive Feature Elimination, - refer to [RFE]_. - - .. [RFE] http://scikit-learn.org/stable/modules/feature_selection.html#recursive-feature-elimination - - """ - - # Extract model data. - - X_train = model.X_train - y_train = model.y_train - - # Extract model parameters. - - rfe_step = model.specs['rfe_step'] - verbosity = model.specs['verbosity'] - estimator = model.estimators[algo] - - # Perform Recursive Feature Elimination - - logger.info("Recursive Feature Elimination") - rfe = RFE(estimator, step=rfe_step, verbose=verbosity) - start = time() - selector = rfe.fit(X_train, y_train) - logger.info("RFE took %.2f seconds for step %d", - (time() - start), rfe_step) - logger.info("Algorithm: %s, Selected Features: %d, Ranking: %s", - algo, selector.n_features_, selector.ranking_) - - # Record the new estimator and support vector - - model.estimators[algo] = selector.estimator_ - model.support[algo] = selector.support_ - - # Return the model with the support vector - return model - - -# -# Function grid_report -# - -def grid_report(results, n_top=3): - r"""Report the top grid search scores. - - Parameters - ---------- - results : dict of numpy arrays - Mean test scores for each grid search iteration. - n_top : int, optional - The number of grid search results to report. - - Returns - ------- - None : None - - """ - for i in range(1, n_top + 1): - candidates = np.flatnonzero(results['rank_test_score'] == i) - for candidate in candidates: - logger.info("Model with rank: {0}".format(i)) - logger.info("Mean validation score: {0:.3f} (std: {1:.3f})".format( - results['mean_test_score'][candidate], - results['std_test_score'][candidate])) - logger.info("Parameters: {0}".format(results['params'][candidate])) - - -# -# Function hyper_grid_search -# - -def hyper_grid_search(model, estimator): - r"""Return the best hyperparameters for a grid search. - - Parameters - ---------- - model : alphapy.Model - The model object with grid search parameters. - estimator : alphapy.Estimator - The estimator containing the hyperparameter grid. - - Returns - ------- - model : alphapy.Model - The model object with the grid search estimator. - - Notes - ----- - To reduce the time required for grid search, use either - randomized grid search with a fixed number of iterations - or a full grid search with subsampling. AlphaPy uses - the scikit-learn Pipeline with feature selection to - reduce the feature space. - - References - ---------- - For more information about grid search, refer to [GRID]_. - - .. [GRID] http://scikit-learn.org/stable/modules/grid_search.html#grid-search - - To learn about pipelines, refer to [PIPE]_. - - .. [PIPE] http://scikit-learn.org/stable/modules/pipeline.html#pipeline - - """ - - # Extract estimator parameters. - - grid = estimator.grid - if not grid: - logger.info("No grid is defined for grid search") - return model - - # Get estimator. - - algo = estimator.algorithm - est = model.estimators[algo] - - # Extract model data. - - try: - support = model.support[algo] - X_train = model.X_train[:, support] - except: - X_train = model.X_train - y_train = model.y_train - - # Extract model parameters. - - cv_folds = model.specs['cv_folds'] - feature_selection = model.specs['feature_selection'] - fs_percentage = model.specs['fs_percentage'] - fs_score_func = model.specs['fs_score_func'] - fs_uni_grid = model.specs['fs_uni_grid'] - gs_iters = model.specs['gs_iters'] - gs_random = model.specs['gs_random'] - gs_sample = model.specs['gs_sample'] - gs_sample_pct = model.specs['gs_sample_pct'] - n_jobs = model.specs['n_jobs'] - scorer = model.specs['scorer'] - verbosity = model.specs['verbosity'] - - # Subsample if necessary to reduce grid search duration. - - if gs_sample: - length = len(X_train) - subset = int(length * gs_sample_pct) - indices = np.random.choice(length, subset, replace=False) - X_train = X_train[indices] - y_train = y_train[indices] - - # Convert the grid to pipeline format - - grid_new = {} - for k, v in grid.items(): - new_key = '__'.join(['est', k]) - grid_new[new_key] = grid[k] - - # Create the pipeline for grid search - - if feature_selection: - # Augment the grid for feature selection. - fs = SelectPercentile(score_func=fs_score_func, - percentile=fs_percentage) - # Combine the feature selection and estimator grids. - fs_grid = dict(fs__percentile=fs_uni_grid) - grid_new.update(fs_grid) - # Create a pipeline with the selected features and estimator. - pipeline = Pipeline([("fs", fs), ("est", est)]) - else: - pipeline = Pipeline([("est", est)]) - - # Create the randomized grid search iterator. - - if gs_random: - logger.info("Randomized Grid Search") - gscv = RandomizedSearchCV(pipeline, param_distributions=grid_new, - n_iter=gs_iters, scoring=scorer, - n_jobs=n_jobs, cv=cv_folds, verbose=verbosity) - else: - logger.info("Full Grid Search") - gscv = GridSearchCV(pipeline, param_grid=grid_new, scoring=scorer, - n_jobs=n_jobs, cv=cv_folds, verbose=verbosity) - - # Fit the randomized search and time it. - - start = time() - gscv.fit(X_train, y_train) - if gs_iters > 0: - logger.info("Grid Search took %.2f seconds for %d candidate" - " parameter settings." % ((time() - start), gs_iters)) - else: - logger.info("Grid Search took %.2f seconds for %d candidate parameter" - " settings." % (time() - start, len(gscv.cv_results_['params']))) - - # Log the grid search scoring statistics. - - grid_report(gscv.cv_results_) - logger.info("Algorithm: %s, Best Score: %.4f, Best Parameters: %s", - algo, gscv.best_score_, gscv.best_params_) - - # Assign the Grid Search estimator for this algorithm - - model.estimators[algo] = gscv - - # Return the model with Grid Search estimators - return model diff --git a/alphapy/plots.py.bak b/alphapy/plots.py.bak deleted file mode 100644 index f25db0d..0000000 --- a/alphapy/plots.py.bak +++ /dev/null @@ -1,1243 +0,0 @@ -################################################################################ -# -# Package : AlphaPy -# Module : plots -# Created : July 11, 2013 -# -# Copyright 2017 ScottFree Analytics LLC -# Mark Conway & Robert D. Scott II -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. -# -################################################################################ - - -# -# Model Plots -# -# 1. Calibration -# 2. Feature Importance -# 3. Learning Curve -# 4. ROC Curve -# 5. Confusion Matrix -# 6. Validation Curve -# 7. Partial Dependence -# 8. Decision Boundary -# -# EDA Plots -# -# 1. Scatter Plot Matrix -# 2. Facet Grid -# 3. Distribution Plot -# 4. Box Plot -# 5. Swarm Plot -# -# Time Series -# -# 1. Time Series -# 2. Candlestick -# - -print(__doc__) - - -# -# Imports -# - -from alphapy.estimators import get_estimators -from alphapy.globals import BSEP, PSEP, SSEP, USEP -from alphapy.globals import ModelType -from alphapy.globals import Partition, datasets -from alphapy.globals import Q1, Q3 -from alphapy.utilities import remove_list_items - -from bokeh.plotting import figure, show, output_file -from itertools import cycle -from itertools import product -import logging -import math -import matplotlib.pyplot as plt -from mpl_toolkits.mplot3d import Axes3D -import numpy as np -import pandas as pd -from scipy import interp -import seaborn as sns -from sklearn.calibration import calibration_curve -from sklearn.ensemble.partial_dependence import partial_dependence -from sklearn.ensemble.partial_dependence import plot_partial_dependence -from sklearn.learning_curve import validation_curve -from sklearn.metrics import auc -from sklearn.metrics import confusion_matrix -from sklearn.metrics import roc_curve -from sklearn.model_selection import cross_val_score -from sklearn.model_selection import learning_curve -from sklearn.model_selection import StratifiedKFold -from sklearn.model_selection import train_test_split - - -# -# Initialize logger -# - -logger = logging.getLogger(__name__) - - -# -# Function get_partition_data -# - -def get_partition_data(model, partition): - r"""Get the X, y pair for a given model and partition - - Parameters - ---------- - model : alphapy.Model - The model object with partition data. - partition : alphapy.Partition - Reference to the dataset. - - Returns - ------- - X : numpy array - The feature matrix. - y : numpy array - The target vector. - - Raises - ------ - TypeError - Partition must be train or test. - - """ - - if partition == Partition.train: - X = model.X_train - y = model.y_train - elif partition == Partition.test: - X = model.X_test - y = model.y_test - else: - raise TypeError('Partition must be train or test') - - return X, y - - -# -# Function generate_plots -# - -def generate_plots(model, partition): - r"""Generate plots while running the pipeline. - - Parameters - ---------- - model : alphapy.Model - The model object with plotting specifications. - partition : alphapy.Partition - Reference to the dataset. - - Returns - ------- - None : None - - """ - - logger.info('='*80) - logger.info("Generating Plots for partition: %s", datasets[partition]) - - # Extract model parameters - - calibration_plot = model.specs['calibration_plot'] - confusion_matrix = model.specs['confusion_matrix'] - importances = model.specs['importances'] - learning_curve = model.specs['learning_curve'] - roc_curve = model.specs['roc_curve'] - - # Generate plots - - if calibration_plot: - plot_calibration(model, partition) - if confusion_matrix: - plot_confusion_matrix(model, partition) - if roc_curve: - plot_roc_curve(model, partition) - if partition == Partition.train: - if learning_curve: - plot_learning_curve(model, partition) - if importances: - plot_importance(model, partition) - - -# -# Function get_plot_directory -# - -def get_plot_directory(model): - r"""Get the plot output directory of a model. - - Parameters - ---------- - model : alphapy.Model - The model object with directory information. - - Returns - ------- - plot_directory : str - The output directory to write the plot. - - """ - directory = model.specs['directory'] - plot_directory = SSEP.join([directory, 'plots']) - return plot_directory - - -# -# Function write_plot -# - -def write_plot(vizlib, plot, plot_type, tag, directory=None): - r"""Save the plot to a file, or display it interactively. - - Parameters - ---------- - vizlib : str - The visualization library: ``'matplotlib'``, ``'seaborn'``, - or ``'bokeh'``. - plot : module - Plotting context, e.g., ``plt``. - plot_type : str - Type of plot to generate. - tag : str - Unique identifier for the plot. - directory : str, optional - The full specification for the directory location. if - ``directory`` is *None*, then the plot is displayed - interactively. - - Returns - ------- - None : None. - - Raises - ------ - ValueError - Unrecognized data visualization library. - - References - ---------- - - Visualization Libraries: - - * Matplotlib : http://matplotlib.org/ - * Seaborn : https://seaborn.pydata.org/ - * Bokeh : http://bokeh.pydata.org/en/latest/ - - """ - - # Validate visualization library - - if (vizlib == 'matplotlib' or - vizlib == 'seaborn' or - vizlib == 'bokeh'): - # supported library - pass - elif vizlib == 'plotly': - raise ValueError("Unsupported data visualization library: %s" % vizlib) - else: - raise ValueError("Unrecognized data visualization library: %s" % vizlib) - - # Save or display the plot - - if directory: - if vizlib == 'bokeh': - file_only = ''.join([plot_type, USEP, tag, '.html']) - else: - file_only = ''.join([plot_type, USEP, tag, '.png']) - file_all = SSEP.join([directory, file_only]) - logger.info("Writing plot to %s", file_all) - if vizlib == 'matplotlib': - plot.tight_layout() - plot.savefig(file_all) - elif vizlib == 'seaborn': - plot.savefig(file_all) - else: - output_file(file_all, title=tag) - show(plot) - else: - if vizlib == 'bokeh': - show(plot) - else: - plot.plot() - - -# -# Function plot_calibration -# - -def plot_calibration(model, partition): - r"""Display scikit-learn calibration plots. - - Parameters - ---------- - model : alphapy.Model - The model object with plotting specifications. - partition : alphapy.Partition - Reference to the dataset. - - Returns - ------- - None : None - - References - ---------- - Code excerpts from authors: - - * Alexandre Gramfort - * Jan Hendrik Metzen - - http://scikit-learn.org/stable/auto_examples/calibration/plot_calibration_curve.html#sphx-glr-auto-examples-calibration-plot-calibration-curve-py - - """ - - logger.info("Generating Calibration Plot") - - # For classification only - - if model.specs['model_type'] != ModelType.classification: - logger.info('Calibration plot is for classification only') - return None - - # Get X, Y for correct partition - - X, y = get_partition_data(model, partition) - - plt.style.use('classic') - plt.figure(figsize=(10, 10)) - ax1 = plt.subplot2grid((3, 1), (0, 0), rowspan=2) - ax2 = plt.subplot2grid((3, 1), (2, 0)) - - ax1.plot([0, 1], [0, 1], "k:", label="Perfectly Calibrated") - for algo in model.algolist: - logger.info("Calibration for Algorithm: %s", algo) - clf = model.estimators[algo] - if hasattr(clf, "predict_proba"): - prob_pos = model.probas[(algo, partition)] - else: # use decision function - prob_pos = clf.decision_function(X) - prob_pos = \ - (prob_pos - prob_pos.min()) / (prob_pos.max() - prob_pos.min()) - fraction_of_positives, mean_predicted_value = \ - calibration_curve(y, prob_pos, n_bins=10) - ax1.plot(mean_predicted_value, fraction_of_positives, "s-", - label="%s" % (algo, )) - ax2.hist(prob_pos, range=(0, 1), bins=10, label=algo, - histtype="step", lw=2) - - ax1.set_ylabel("Fraction of Positives") - ax1.set_ylim([-0.05, 1.05]) - ax1.legend(loc="lower right") - ax1.set_title('Calibration Plots [Reliability Curve]') - - ax2.set_xlabel("Mean Predicted Value") - ax2.set_ylabel("Count") - ax2.legend(loc="upper center", ncol=2) - - plot_dir = get_plot_directory(model) - pstring = datasets[partition] - write_plot('matplotlib', plt, 'calibration', pstring, plot_dir) - - -# -# Function plot_importances -# - -def plot_importance(model, partition): - r"""Display scikit-learn feature importances. - - Parameters - ---------- - model : alphapy.Model - The model object with plotting specifications. - partition : alphapy.Partition - Reference to the dataset. - - Returns - ------- - None : None - - References - ---------- - - http://scikit-learn.org/stable/auto_examples/ensemble/plot_forest_importances.html - - """ - - logger.info("Generating Feature Importance Plots") - plot_dir = get_plot_directory(model) - pstring = datasets[partition] - - # Get X, Y for correct partition - - X, y = get_partition_data(model, partition) - - # For each algorithm that has importances, generate the plot. - - n_top = 10 - for algo in model.algolist: - logger.info("Feature Importances for Algorithm: %s", algo) - try: - importances = model.importances[algo] - # forest was input parameter - indices = np.argsort(importances)[::-1] - # log the feature ranking - logger.info("Feature Ranking:") - for f in range(n_top): - logger.info("%d. Feature %d (%f)" % (f + 1, indices[f], importances[indices[f]])) - # plot the feature importances - title = BSEP.join([algo, "Feature Importances [", pstring, "]"]) - plt.style.use('classic') - plt.figure() - plt.title(title) - plt.bar(range(n_top), importances[indices][:n_top], color="b", align="center") - plt.xticks(range(n_top), indices[:n_top]) - plt.xlim([-1, n_top]) - # save the plot - tag = USEP.join([pstring, algo]) - write_plot('matplotlib', plt, 'feature_importance', tag, plot_dir) - except: - logger.info("%s does not have feature importances", algo) - - -# -# Function plot_learning_curve -# - -def plot_learning_curve(model, partition): - r"""Generate learning curves for a given partition. - - Parameters - ---------- - model : alphapy.Model - The model object with plotting specifications. - partition : alphapy.Partition - Reference to the dataset. - - Returns - ------- - None : None - - References - ---------- - - http://scikit-learn.org/stable/auto_examples/ensemble/plot_forest_importances.html - - """ - - logger.info("Generating Learning Curves") - plot_dir = get_plot_directory(model) - pstring = datasets[partition] - - # Extract model parameters. - - cv_folds = model.specs['cv_folds'] - n_jobs = model.specs['n_jobs'] - seed = model.specs['seed'] - shuffle = model.specs['shuffle'] - verbosity = model.specs['verbosity'] - - # Get original estimators - - estimators = get_estimators(model) - - # Get X, Y for correct partition. - - X, y = get_partition_data(model, partition) - - # Set cross-validation parameters to get mean train and test curves. - - cv = StratifiedKFold(n_splits=cv_folds, shuffle=shuffle, random_state=seed) - - # Plot a learning curve for each algorithm. - - ylim = (0.4, 1.01) - - for algo in model.algolist: - logger.info("Learning Curve for Algorithm: %s", algo) - # get estimator - est = estimators[algo].estimator - # plot learning curve - title = BSEP.join([algo, "Learning Curve [", pstring, "]"]) - # set up plot - plt.style.use('classic') - plt.figure() - plt.title(title) - if ylim is not None: - plt.ylim(*ylim) - plt.xlabel("Training Examples") - plt.ylabel("Score") - # call learning curve function - train_sizes=np.linspace(0.1, 1.0, cv_folds) - train_sizes, train_scores, test_scores = \ - learning_curve(est, X, y, train_sizes=train_sizes, cv=cv, - n_jobs=n_jobs, verbose=verbosity) - train_scores_mean = np.mean(train_scores, axis=1) - train_scores_std = np.std(train_scores, axis=1) - test_scores_mean = np.mean(test_scores, axis=1) - test_scores_std = np.std(test_scores, axis=1) - plt.grid() - # plot data - plt.fill_between(train_sizes, train_scores_mean - train_scores_std, - train_scores_mean + train_scores_std, alpha=0.1, - color="r") - plt.fill_between(train_sizes, test_scores_mean - test_scores_std, - test_scores_mean + test_scores_std, alpha=0.1, color="g") - plt.plot(train_sizes, train_scores_mean, 'o-', color="r", - label="Training Score") - plt.plot(train_sizes, test_scores_mean, 'o-', color="g", - label="Cross-Validation Score") - plt.legend(loc="lower right") - # save the plot - tag = USEP.join([pstring, algo]) - write_plot('matplotlib', plt, 'learning_curve', tag, plot_dir) - - -# -# Function plot_roc_curve -# - -def plot_roc_curve(model, partition): - r"""Display ROC Curves with Cross-Validation. - - Parameters - ---------- - model : alphapy.Model - The model object with plotting specifications. - partition : alphapy.Partition - Reference to the dataset. - - Returns - ------- - None : None - - References - ---------- - - http://scikit-learn.org/stable/modules/model_evaluation.html#receiver-operating-characteristic-roc - - """ - - logger.info("Generating ROC Curves") - pstring = datasets[partition] - - # For classification only - - if model.specs['model_type'] != ModelType.classification: - logger.info('ROC Curves are for classification only') - return None - - # Get X, Y for correct partition. - - X, y = get_partition_data(model, partition) - - # Initialize plot parameters. - - plt.style.use('classic') - plt.figure() - colors = cycle(['cyan', 'indigo', 'seagreen', 'yellow', 'blue', 'darkorange']) - lw = 2 - - # Plot a ROC Curve for each algorithm. - - for algo in model.algolist: - logger.info("ROC Curve for Algorithm: %s", algo) - # get estimator - estimator = model.estimators[algo] - # compute ROC curve and ROC area for each class - probas = model.probas[(algo, partition)] - fpr, tpr, _ = roc_curve(y, probas) - roc_auc = auc(fpr, tpr) - plt.plot(fpr, tpr, lw=lw, label='%s (area = %0.2f)' % (algo, roc_auc)) - - # draw the luck line - plt.plot([0, 1], [0, 1], linestyle='--', color='k', label='Luck') - # define plot characteristics - plt.xlim([-0.05, 1.05]) - plt.ylim([-0.05, 1.05]) - plt.xlabel('False Positive Rate') - plt.ylabel('True Positive Rate') - title = BSEP.join([algo, "ROC Curve [", pstring, "]"]) - plt.title(title) - plt.legend(loc="lower right") - # save chart - plot_dir = get_plot_directory(model) - write_plot('matplotlib', plt, 'roc_curve', pstring, plot_dir) - - -# -# Function plot_confusion_matrix -# - -def plot_confusion_matrix(model, partition): - r"""Draw the confusion matrix. - - Parameters - ---------- - model : alphapy.Model - The model object with plotting specifications. - partition : alphapy.Partition - Reference to the dataset. - - Returns - ------- - None : None - - References - ---------- - - http://scikit-learn.org/stable/modules/model_evaluation.html#confusion-matrix - - """ - - logger.info("Generating Confusion Matrices") - plot_dir = get_plot_directory(model) - pstring = datasets[partition] - - # For classification only - - if model.specs['model_type'] != ModelType.classification: - logger.info('Confusion Matrix is for classification only') - return None - - # Get X, Y for correct partition. - - X, y = get_partition_data(model, partition) - - for algo in model.algolist: - logger.info("Confusion Matrix for Algorithm: %s", algo) - # get predictions for this partition - y_pred = model.preds[(algo, partition)] - # compute confusion matrix - cm = confusion_matrix(y, y_pred) - logger.info('Confusion Matrix:') - logger.info('%s', cm) - # initialize plot - np.set_printoptions(precision=2) - plt.style.use('classic') - plt.figure() - # plot the confusion matrix - cmap = plt.cm.Blues - plt.imshow(cm, interpolation='nearest', cmap=cmap) - title = BSEP.join([algo, "Confusion Matrix [", pstring, "]"]) - plt.title(title) - plt.colorbar() - # set up x and y axes - y_values, y_counts = np.unique(y, return_counts=True) - tick_marks = np.arange(len(y_values)) - plt.xticks(tick_marks, y_values, rotation=45) - plt.yticks(tick_marks, y_values) - # normalize confusion matrix - cmn = cm.astype('float') / cm.sum(axis=1)[:, np.newaxis] - # place text in square of confusion matrix - thresh = (cm.max() + cm.min()) / 2.0 - for i, j in product(range(cm.shape[0]), range(cm.shape[1])): - cmr = round(cmn[i, j], 3) - plt.text(j, i, cmr, - horizontalalignment="center", - color="white" if cm[i, j] > thresh else "black") - # labels - plt.tight_layout() - plt.ylabel('True Label') - plt.xlabel('Predicted Label') - # save the chart - tag = USEP.join([pstring, algo]) - write_plot('matplotlib', plt, 'confusion', tag, plot_dir) - - -# -# Function plot_validation_curve -# - -def plot_validation_curve(model, partition, pname, prange): - r"""Generate scikit-learn validation curves. - - Parameters - ---------- - model : alphapy.Model - The model object with plotting specifications. - partition : alphapy.Partition - Reference to the dataset. - pname : str - Name of the hyperparameter to test. - prange : numpy array - The values of the hyperparameter that will be evaluated. - - Returns - ------- - None : None - - References - ---------- - - http://scikit-learn.org/stable/auto_examples/model_selection/plot_validation_curve.html#sphx-glr-auto-examples-model-selection-plot-validation-curve-py - - """ - - logger.info("Generating Validation Curves") - plot_dir = get_plot_directory(model) - pstring = datasets[partition] - - # Extract model parameters. - - cv_folds = model.specs['cv_folds'] - n_jobs = model.specs['n_jobs'] - scorer = model.specs['scorer'] - verbosity = model.specs['verbosity'] - - # Get X, Y for correct partition. - - X, y = get_partition_data(model, partition) - - # Define plotting constants. - - spacing = 0.5 - alpha = 0.2 - - # Calculate a validation curve for each algorithm. - - for algo in model.algolist: - logger.info("Algorithm: %s", algo) - # get estimator - estimator = model.estimators[algo] - # set up plot - train_scores, test_scores = validation_curve( - estimator, X, y, param_name=pname, param_range=prange, - cv=cv_folds, scoring=scorer, n_jobs=n_jobs) - train_scores_mean = np.mean(train_scores, axis=1) - train_scores_std = np.std(train_scores, axis=1) - test_scores_mean = np.mean(test_scores, axis=1) - test_scores_std = np.std(test_scores, axis=1) - # set up figure - plt.style.use('classic') - plt.figure() - # plot learning curves - title = BSEP.join([algo, "Validation Curve [", pstring, "]"]) - plt.title(title) - # x-axis - x_min, x_max = min(prange) - spacing, max(prange) + spacing - plt.xlabel(pname) - plt.xlim(x_min, x_max) - # y-axis - plt.ylabel("Score") - plt.ylim(0.0, 1.1) - # plot scores - plt.plot(prange, train_scores_mean, label="Training Score", color="r") - plt.fill_between(prange, train_scores_mean - train_scores_std, - train_scores_mean + train_scores_std, alpha=alpha, color="r") - plt.plot(prange, test_scores_mean, label="Cross-Validation Score", - color="g") - plt.fill_between(prange, test_scores_mean - test_scores_std, - test_scores_mean + test_scores_std, alpha=alpha, color="g") - plt.legend(loc="best") # save the plot - tag = USEP.join([pstring, algo]) - write_plot('matplotlib', plt, 'validation_curve', tag, plot_dir) - - -# -# Function plot_boundary -# - -def plot_boundary(model, partition, f1=0, f2=1): - r"""Display a comparison of classifiers - - Parameters - ---------- - model : alphapy.Model - The model object with plotting specifications. - partition : alphapy.Partition - Reference to the dataset. - f1 : int - Number of the first feature to compare. - f2 : int - Number of the second feature to compare. - - Returns - ------- - None : None - - References - ---------- - Code excerpts from authors: - - * Gael Varoquaux - * Andreas Muller - - http://scikit-learn.org/stable/auto_examples/classification/plot_classifier_comparison.html - - """ - - logger.info("Generating Boundary Plots") - pstring = datasets[partition] - - # For classification only - - if model.specs['model_type'] != ModelType.classification: - logger.info('Boundary Plots are for classification only') - return None - - # Get X, Y for correct partition - - X, y = get_partition_data(model, partition) - - # Subset for the two boundary features - - X = X[[f1, f2]] - - # Initialize plot - - n_classifiers = len(model.algolist) - plt.figure(figsize=(3 * 2, n_classifiers * 2)) - plt.subplots_adjust(bottom=.2, top=.95) - - xx = np.linspace(3, 9, 100) - yy = np.linspace(1, 5, 100).T - xx, yy = np.meshgrid(xx, yy) - Xfull = np.c_[xx.ravel(), yy.ravel()] - - # Plot each classification probability - - for index, name in enumerate(model.algolist): - # predictions - y_pred = model.preds[(algo, partition)] - classif_rate = np.mean(y_pred.ravel() == y.ravel()) * 100 - logger.info("Classification Rate for %s : %f " % (name, classif_rate)) - # probabilities - probas = model.probas[(algo, partition)] - n_classes = np.unique(y_pred).size - # plot each class - for k in range(n_classes): - plt.subplot(n_classifiers, n_classes, index * n_classes + k + 1) - plt.title("Class %d" % k) - if k == 0: - plt.ylabel(name) - imshow_handle = plt.imshow(probas[:, k].reshape((100, 100)), - extent=(3, 9, 1, 5), origin='lower') - plt.xticks(()) - plt.yticks(()) - idx = (y_pred == k) - if idx.any(): - plt.scatter(X[idx, 0], X[idx, 1], marker='o', c='k') - - # Plot the probability color bar - - ax = plt.axes([0.15, 0.04, 0.7, 0.05]) - plt.title("Probability") - plt.colorbar(imshow_handle, cax=ax, orientation='horizontal') - - # Save the plot - plot_dir = get_plot_directory(model) - write_plot('matplotlib', figure, 'boundary', pstring, plot_dir) - - -# -# Function plot_partial_dependence -# - -def plot_partial_dependence(est, X, features, fnames, tag, - n_jobs=-1, verbosity=0, directory=None): - r"""Display a Partial Dependence Plot. - - Parameters - ---------- - est : estimator - The scikit-learn estimator for calculating partial dependence. - X : numpy array - The data on which the estimator was trained. - features : list of int - Feature numbers of ``X``. - fnames : list of str - The feature names to plot. - tag : str - Unique identifier for the plot - n_jobs : int, optional - The maximum number of parallel jobs. - verbosity : int, optional - The amount of logging from 0 (minimum) and higher. - directory : str - Directory where the plot will be stored. - - Returns - ------- - None : None. - - References - ---------- - - http://scikit-learn.org/stable/auto_examples/ensemble/plot_partial_dependence.html#sphx-glr-auto-examples-ensemble-plot-partial-dependence-py - - """ - - logger.info("Generating Partial Dependence Plot") - - # Plot partial dependence - - fig, axs = plot_partial_dependence(est, X, features, feature_names=fnames, - grid_resolution=50, n_jobs=n_jobs, - verbose=verbosity) - title = "Partial Dependence Plot" - fig.suptitle(title) - plt.subplots_adjust(top=0.9) # tight_layout causes overlap with suptitle - - # Save the plot - write_plot(model, 'matplotlib', plt, 'partial_dependence', tag, directory) - - -# -# Function plot_scatter -# - -def plot_scatter(df, features, target, tag='eda', directory=None): - r"""Plot a scatterplot matrix, also known as a pair plot. - - Parameters - ---------- - df : pandas.DataFrame - The dataframe containing the features. - features: list of str - The features to compare in the scatterplot. - target : str - The target variable for contrast. - tag : str - Unique identifier for the plot. - directory : str, optional - The full specification of the plot location. - - Returns - ------- - None : None. - - References - ---------- - - https://seaborn.pydata.org/examples/scatterplot_matrix.html - - """ - - logger.info("Generating Scatter Plot") - - # Get the feature subset - - features.append(target) - df = df[features] - - # Generate the pair plot - - sns.set() - sns_plot = sns.pairplot(df, hue=target) - - # Save the plot - write_plot('seaborn', sns_plot, 'scatter_plot', tag, directory) - - -# -# Function plot_facet_grid -# - -def plot_facet_grid(df, target, frow, fcol, tag='eda', directory=None): - r"""Plot a Seaborn faceted histogram grid. - - Parameters - ---------- - df : pandas.DataFrame - The dataframe containing the features. - target : str - The target variable for contrast. - frow : list of str - Feature names for the row elements of the grid. - fcol : list of str - Feature names for the column elements of the grid. - tag : str - Unique identifier for the plot. - directory : str, optional - The full specification of the plot location. - - Returns - ------- - None : None. - - References - ---------- - - http://seaborn.pydata.org/generated/seaborn.FacetGrid.html - - """ - - logger.info("Generating Facet Grid") - - # Calculate the number of bins using the Freedman-Diaconis rule. - - tlen = len(df[target]) - tmax = df[target].max() - tmin = df[target].min() - trange = tmax - tmin - iqr = df[target].quantile(Q3) - df[target].quantile(Q1) - h = 2 * iqr * (tlen ** (-1/3)) - nbins = math.ceil(trange / h) - - # Generate the pair plot - - sns.set(style="darkgrid") - - fg = sns.FacetGrid(df, row=frow, col=fcol, margin_titles=True) - bins = np.linspace(tmin, tmax, nbins) - fg.map(plt.hist, target, color="steelblue", bins=bins, lw=0) - - # Save the plot - write_plot('seaborn', fg, 'facet_grid', tag, directory) - - -# -# Function plot_distribution -# - -def plot_distribution(df, target, tag='eda', directory=None): - r"""Display a Distribution Plot. - - Parameters - ---------- - df : pandas.DataFrame - The dataframe containing the ``target`` feature. - target : str - The target variable for the distribution plot. - tag : str - Unique identifier for the plot. - directory : str, optional - The full specification of the plot location. - - Returns - ------- - None : None. - - References - ---------- - - http://seaborn.pydata.org/generated/seaborn.distplot.html - - """ - - logger.info("Generating Distribution Plot") - - # Generate the distribution plot - - dist_plot = sns.distplot(df[target]) - dist_fig = dist_plot.get_figure() - - # Save the plot - write_plot('seaborn', dist_fig, 'distribution_plot', tag, directory) - - -# -# Function plot_box -# - -def plot_box(df, x, y, hue, tag='eda', directory=None): - r"""Display a Box Plot. - - Parameters - ---------- - df : pandas.DataFrame - The dataframe containing the ``x`` and ``y`` features. - x : str - Variable name in ``df`` to display along the x-axis. - y : str - Variable name in ``df`` to display along the y-axis. - hue : str - Variable name to be used as hue, i.e., another data dimension. - tag : str - Unique identifier for the plot. - directory : str, optional - The full specification of the plot location. - - Returns - ------- - None : None. - - References - ---------- - - http://seaborn.pydata.org/generated/seaborn.boxplot.html - - """ - - logger.info("Generating Box Plot") - - # Generate the box plot - - box_plot = sns.boxplot(x=x, y=y, hue=hue, data=df) - sns.despine(offset=10, trim=True) - box_fig = box_plot.get_figure() - - # Save the plot - write_plot('seaborn', box_fig, 'box_plot', tag, directory) - - -# -# Function plot_swarm -# - -def plot_swarm(df, x, y, hue, tag='eda', directory=None): - r"""Display a Swarm Plot. - - Parameters - ---------- - df : pandas.DataFrame - The dataframe containing the ``x`` and ``y`` features. - x : str - Variable name in ``df`` to display along the x-axis. - y : str - Variable name in ``df`` to display along the y-axis. - hue : str - Variable name to be used as hue, i.e., another data dimension. - tag : str - Unique identifier for the plot. - directory : str, optional - The full specification of the plot location. - - Returns - ------- - None : None. - - References - ---------- - - http://seaborn.pydata.org/generated/seaborn.swarmplot.html - - """ - - logger.info("Generating Swarm Plot") - - # Generate the swarm plot - - swarm_plot = sns.swarmplot(x=x, y=y, hue=hue, data=df) - swarm_fig = swarm_plot.get_figure() - - # Save the plot - write_plot('seaborn', swarm_fig, 'swarm_plot', tag, directory) - - -# -# Time Series Plots -# - - -# -# Function plot_time_series -# - -def plot_time_series(df, target, tag='eda', directory=None): - r"""Plot time series data. - - Parameters - ---------- - df : pandas.DataFrame - The dataframe containing the ``target`` feature. - target : str - The target variable for the time series plot. - tag : str - Unique identifier for the plot. - directory : str, optional - The full specification of the plot location. - - Returns - ------- - None : None. - - References - ---------- - - http://seaborn.pydata.org/generated/seaborn.tsplot.html - - """ - - logger.info("Generating Time Series Plot") - - # Generate the time series plot - - ts_plot = sns.tsplot(data=df[target]) - ts_fig = ts_plot.get_figure() - - # Save the plot - write_plot('seaborn', ts_fig, 'time_series_plot', tag, directory) - - -# -# Function plot_candlestick -# - -def plot_candlestick(df, symbol, datecol='date', directory=None): - r"""Plot time series data. - - Parameters - ---------- - df : pandas.DataFrame - The dataframe containing the ``target`` feature. - symbol : str - Unique identifier of the data to plot. - datecol : str, optional - The name of the date column. - directory : str, optional - The full specification of the plot location. - - Returns - ------- - None : None. - - Notes - ----- - The dataframe ``df`` must contain these columns: - - * ``open`` - * ``high`` - * ``low`` - * ``close`` - - References - ---------- - - http://bokeh.pydata.org/en/latest/docs/gallery/candlestick.html - - """ - - df[datecol] = pd.to_datetime(df[datecol]) - - mids = (df.open + df.close) / 2 - spans = abs(df.close - df.open) - - inc = df.close > df.open - dec = df.open > df.close - w = 12 * 60 * 60 * 1000 # half day in ms - - TOOLS = "pan, wheel_zoom, box_zoom, reset, save" - - p = figure(x_axis_type="datetime", tools=TOOLS, plot_width=1000, toolbar_location="left") - - p.title = BSEP.join([symbol.upper(), "Candlestick"]) - p.xaxis.major_label_orientation = math.pi / 4 - p.grid.grid_line_alpha = 0.3 - - p.segment(df.date, df.high, df.date, df.low, color="black") - p.rect(df.date[inc], mids[inc], w, spans[inc], fill_color="#D5E1DD", line_color="black") - p.rect(df.date[dec], mids[dec], w, spans[dec], fill_color="#F2583E", line_color="black") - - # Save the plot - write_plot('bokeh', p, 'candlestick_chart', symbol, directory) diff --git a/alphapy/portfolio.py.bak b/alphapy/portfolio.py.bak deleted file mode 100644 index 4efb518..0000000 --- a/alphapy/portfolio.py.bak +++ /dev/null @@ -1,1164 +0,0 @@ -################################################################################ -# -# Package : AlphaPy -# Module : portfolio -# Created : July 11, 2013 -# -# Copyright 2017 ScottFree Analytics LLC -# Mark Conway & Robert D. Scott II -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. -# -################################################################################ - - -# -# Imports -# - -from alphapy.frame import Frame -from alphapy.frame import frame_name -from alphapy.frame import read_frame -from alphapy.frame import write_frame -from alphapy.globals import MULTIPLIERS, SSEP -from alphapy.globals import Orders -from alphapy.space import Space - -import logging -import math -import numpy as np -from pandas import DataFrame -from pandas import date_range -from pandas import Series - - -# -# Initialize logger -# - -logger = logging.getLogger(__name__) - - -# -# Function portfolio_name -# - -def portfolio_name(group_name, tag): - """ - Return the name of the portfolio. - - Parameters - ---------- - group_name : str - The group represented in the portfolio. - tag : str - A unique identifier. - - Returns - ------- - port_name : str - Portfolio name. - - """ - port_name = '.'.join([group_name, tag, "portfolio"]) - return port_name - - -# -# Class Portfolio -# - -class Portfolio(): - """Create a new portfolio with a unique name. All portfolios - are stored in ``Portfolio.portfolios``. - - Parameters - ---------- - group_name : str - The group represented in the portfolio. - tag : str - A unique identifier. - space : alphapy.Space, optional - Namespace for the portfolio. - maxpos : int, optional - The maximum number of positions. - posby : str, optional - The denominator for position sizing. - kopos : int, optional - The number of positions to kick out from the portfolio. - koby : str, optional - The "kick out" criteria. For example, a ``koby`` value - of '-profit' means the three least profitable positions - will be closed. - restricted : bool, optional - If ``True``, then the portfolio is limited to a maximum - number of positions ``maxpos``. - weightby : str, optional - The weighting variable to balance the portfolio, e.g., - by closing price, by volatility, or by any column. - startcap : float, optional - The amount of starting capital. - margin : float, optional - The amount of margin required, expressed as a fraction. - mincash : float, optional - Minimum amount of cash on hand, expressed as a fraction - of the total portfolio value. - fixedfrac : float, optional - The fixed fraction for any given position. - maxloss : float, optional - Stop loss for any given position. - - Attributes - ---------- - portfolios : dict - Class variable for storing all known portfolios - value : float - Class variable for storing all known portfolios - netprofit : float - Net profit ($) since previous valuation. - netreturn : float - Net return (%) since previous valuation - totalprofit : float - Total profit ($) since inception. - totalreturn : float - Total return (%) since inception. - - """ - - # class variable to track all portfolios - - portfolios = {} - - # __new__ - - def __new__(cls, - group_name, - tag, - space = Space(), - maxpos = 10, - posby = 'close', - kopos = 0, - koby = '-profit', - restricted = False, - weightby = 'quantity', - startcap = 100000, - margin = 0.5, - mincash = 0.2, - fixedfrac = 0.1, - maxloss = 0.1): - # create portfolio name - pn = portfolio_name(group_name, tag) - if not pn in Portfolio.portfolios: - return super(Portfolio, cls).__new__(cls) - else: - logger.info("Portfolio %s already exists", pn) - - # __init__ - - def __init__(self, - group_name, - tag, - space = Space(), - maxpos = 10, - posby = 'close', - kopos = 0, - koby = '-profit', - restricted = False, - weightby = 'quantity', - startcap = 100000, - margin = 0.5, - mincash = 0.2, - fixedfrac = 0.1, - maxloss = 0.1): - # initialization - self.group_name = group_name - self.tag = tag - self.space = space - self.positions = {} - self.startdate = None - self.enddate = None - self.npos = 0 - self.maxpos = maxpos - self.posby = posby - self.kopos = kopos - self.koby = koby - self.restricted = restricted - self.weightby = weightby - self.weights = [] - self.startcap = startcap - self.cash = startcap - self.margin = margin - self.mincash = mincash - self.fixedfrac = fixedfrac - self.maxloss = maxloss - self.value = startcap - self.netprofit = 0.0 - self.netreturn = 0.0 - self.totalprofit = 0.0 - self.totalreturn = 0.0 - # add portfolio to portfolios list - pn = portfolio_name(group_name, tag) - Portfolio.portfolios[pn] = self - - # __str__ - - def __str__(self): - return portfolio_name(self.group_name, self.tag) - - -# -# Class Position -# - -class Position: - """Create a new position in the portfolio. - - Parameters - ---------- - portfolio : alphaPy.portfolio - The portfolio that will contain the position. - name : str - A unique identifier such as a stock symbol. - opendate : datetime - Date the position is opened. - - Attributes - ---------- - date : timedate - Current date of the position. - name : str - A unique identifier. - status : str - State of the position: ``'opened'`` or ``'closed'``. - mpos : str - Market position ``'long'`` or ``'short'``. - quantity : float - The net size of the position. - price : float - The current price of the instrument. - value : float - The total dollar value of the position. - profit : float - The net profit of the current position. - netreturn : float - The Return On Investment (ROI), or net return. - opened : datetime - Date the position is opened. - held : int - The holding period since the position was opened. - costbasis : float - Overall cost basis. - trades : list of Trade - The executed trades for the position so far. - ntrades : int - Total number of trades. - pdata : pandas DataFrame - Price data for the given ``name``. - multiplier : float - Multiple for instrument type (e.g., 1.0 for stocks). - - """ - - # __init__ - - def __init__(self, - portfolio, - name, - opendate): - space = portfolio.space - self.date = opendate - self.name = name - self.status = 'opened' - self.mpos = 'flat' - self.quantity = 0 - self.price = 0.0 - self.value = 0.0 - self.profit = 0.0 - self.netreturn = 0.0 - self.opened = opendate - self.held = 0 - self.costbasis = 0.0 - self.trades = [] - self.ntrades = 0 - self.pdata = Frame.frames[frame_name(name, space)].df - self.multiplier = MULTIPLIERS[space.subject] - - # __str__ - - def __str__(self): - return self.name - - -# -# Class Trade -# - -class Trade: - """Initiate a trade. - - Parameters - ---------- - name : str - The symbol to trade. - order : alphapy.Orders - Long or short trade for entry or exit. - quantity : int - The quantity for the order. - price : str - The execution price of the trade. - tdate : datetime - The date and time of the trade. - - Attributes - ---------- - states : list of str - Trade state names for a dataframe. - - """ - - states = ['name', 'order', 'quantity', 'price'] - - # __init__ - - def __init__(self, - name, - order, - quantity, - price, - tdate): - self.name = name - self.order = order - self.quantity = float(quantity) - self.price = float(price) - self.tdate = tdate - - -# -# Function add_position -# - -def add_position(p, name, pos): - r"""Add a position to a portfolio. - - Parameters - ---------- - p : alphapy.Portfolio - Portfolio that will hold the position. - name : int - Unique identifier for the position, e.g., a stock symbol. - pos : alphapy.Position - New position to add to the portfolio. - - Returns - ------- - p : alphapy.Portfolio - Portfolio with the new position. - - """ - if name not in p.positions: - p.positions[name] = pos - return p - - -# -# Function remove_position -# - -def remove_position(p, name): - r"""Remove a position from a portfolio by name. - - Parameters - ---------- - p : alphapy.Portfolio - Portfolio with the current position. - name : int - Unique identifier for the position, e.g., a stock symbol. - - Returns - ------- - p : alphapy.Portfolio - Portfolio with the deleted position. - - """ - del p.positions[name] - return p - - -# -# Function valuate_position -# - -def valuate_position(position, tdate): - r"""Valuate the position for the given date. - - Parameters - ---------- - position : alphapy.Position - The position to be valued. - tdate : timedate - Date to value the position. - - Returns - ------- - position : alphapy.Position - New value of the position. - - Notes - ----- - - An Example of Cost Basis - - ======== ====== ====== ====== - Date Shares Price Amount - ======== ====== ====== ====== - 11/09/16 +100 10.0 1,000 - 12/14/16 +200 15.0 3,000 - 04/05/17 -500 20.0 10,000 - -------- ------ ------ ------ - All 800 14,000 - ======== ====== ====== ====== - - The cost basis is calculated as the total value of all - trades (14,000) divided by the total number of shares - traded (800), so 14,000 / 800 = 17.5, and the net position - is -200. - - """ - # get current price - pdata = position.pdata - if tdate in pdata.index: - cp = float(pdata.ix[tdate]['close']) - # start valuation - multiplier = position.multiplier - netpos = 0 - tts = 0 # total traded shares - ttv = 0 # total traded value - totalprofit = 0.0 - for trade in position.trades: - tq = trade.quantity - netpos = netpos + tq - tts = tts + abs(tq) - tp = trade.price - pfactor = tq * multiplier - cv = pfactor * cp - cvabs = abs(cv) - ttv = ttv + cvabs - ev = pfactor * tp - totalprofit = totalprofit + cv - ev - position.quantity = netpos - position.price = cp - position.value = abs(netpos) * multiplier * cp - position.profit = totalprofit - position.costbasis = ttv / tts - position.netreturn = totalprofit / cvabs - 1.0 - return position - - -# -# Function update_position -# - -def update_position(position, trade): - r"""Add the new trade to the position and revalue. - - Parameters - ---------- - position : alphapy.Position - The position to be update. - trade : alphapy.Trade - Trade for updating the position. - - Returns - ------- - position : alphapy.Position - New value of the position. - - """ - position.trades.append(trade) - position.ntrades = position.ntrades + 1 - position.date = trade.tdate - position.held = trade.tdate - position.opened - position = valuate_position(position, trade.tdate) - if position.quantity > 0: - position.mpos = 'long' - if position.quantity < 0: - position.mpos = 'short' - return position - - -# -# Function close_position -# - -def close_position(p, position, tdate): - r"""Close the position and remove it from the portfolio. - - Parameters - ---------- - p : alphapy.Portfolio - Portfolio holding the position. - position : alphapy.Position - Position to close. - tdate : datetime - The date for pricing the closed position. - - Returns - ------- - p : alphapy.Portfolio - Portfolio with the removed position. - - """ - pq = position.quantity - # if necessary, put on an offsetting trade - if pq != 0: - tradesize = -pq - position.date = tdate - pdata = position.pdata - cp = pdata.ix[tdate]['close'] - newtrade = Trade(position.name, tradesize, cp, tdate) - p = update_portfolio(p, position, newtrade) - position.quantity = 0 - position.status = 'closed' - p = remove_position(p, position.name) - return p - - -# -# Function deposit_portfolio -# - -def deposit_portfolio(p, cash, tdate): - r"""Deposit cash into a given portfolio. - - Parameters - ---------- - p : alphapy.Portfolio - Portfolio to accept the deposit. - cash : float - Cash amount to deposit. - tdate : datetime - The date of deposit. - - Returns - ------- - p : alphapy.Portfolio - Portfolio with the added cash. - - """ - p.cash = p.cash + cash - p = valuate_portfolio(p, tdate) - return p - - -# -# Function withdraw_portfolio -# - -def withdraw_portfolio(p, cash, tdate): - r"""Withdraw cash from a given portfolio. - - Parameters - ---------- - p : alphapy.Portfolio - Portfolio to accept the withdrawal. - cash : float - Cash amount to withdraw. - tdate : datetime - The date of withdrawal. - - Returns - ------- - p : alphapy.Portfolio - Portfolio with the withdrawn cash. - - """ - currentcash = p.cash - availcash = currentcash - (p.mincash * p.value) - if cash > availcash: - logger.info("Withdrawal of %s would exceed reserve amount", cash) - else: - p.cash = currentcash - cash - p = valuate_portfolio(p, tdate) - return p - - -# -# Function update_portfolio -# - -def update_portfolio(p, pos, trade): - r"""Update the portfolio positions. - - Parameters - ---------- - p : alphapy.Portfolio - Portfolio holding the position. - pos : alphapy.Position - Position to update. - trade : alphapy.Trade - Trade for updating the position and portfolio. - - Returns - ------- - p : alphapy.Portfolio - Portfolio with the revised position. - - """ - # update position - ppq = abs(pos.quantity) - pos = update_position(pos, trade) - cpq = abs(pos.quantity) - npq = cpq - ppq - # update portfolio - p.date = trade.tdate - multiplier = pos.multiplier - cv = trade.price * multiplier * npq - p.cash -= cv - return p - - -# -# Function delete_portfolio -# - -def delete_portfolio(p): - r"""Delete the portfolio. - - Parameters - ---------- - p : alphapy.Portfolio - Portfolio to delete. - - Returns - ------- - None : None - - """ - positions = p.positions - for key in positions: - p = close_position(p, positions[key]) - del p - - -# -# Function balance -# - -def balance(p, tdate, cashlevel): - r"""Balance the portfolio using a weighting variable. - - Rebalancing is the process of equalizing a portfolio's positions - using some criterion. For example, if a portfolio is *dollar-weighted*, - then one position can increase in proportion to the rest of the - portfolio, i.e., its fraction of the overall portfolio is greater - than the other positions. To make the portfolio "equal dollar", - then some positions have to be decreased and others decreased. - - The rebalancing process is periodic (e.g., once per month) and - generates a series of trades to balance the positions. Other - portfolios are *volatility-weighted* because a more volatile - stock has a greater effect on the beta, i.e., the more volatile - the instrument, the smaller the position size. - - Technically, any type of weight can be used for rebalancing, so - AlphaPy gives the user the ability to specify a ``weightby`` - column name. - - Parameters - ---------- - p : alphapy.Portfolio - Portfolio to rebalance. - tdate : datetime - The rebalancing date. - cashlevel : float - The cash level to maintain during rebalancing. - - Returns - ------- - p : alphapy.Portfolio - The rebalanced portfolio. - - Notes - ----- - - .. warning:: The portfolio management functions ``balance``, - ``kick_out``, and ``stop_loss`` are not part of the - main **StockStream** pipeline, and thus have not been - thoroughly tested. Feel free to exercise the code and - report any issues. - - """ - currentcash = p.cash - mincash = p.mincash - weightby = p.weightby - if not weightby: - weightby = 'close' - p = valuate_portfolio(p, tdate) - pvalue = p.value - cashlevel * p.value - positions = p.positions - bdata = np.ones(len(positions)) - # get weighting variable values - if weightby[0] == "-": - invert = True - weightby = weightby[1:] - else: - invert = False - attrs = filter(lambda aname: not aname.startswith('_'), dir(positions[0])) - for i, pos in enumerate(positions): - if weightby in attrs: - estr = '.'.join('pos', weightby) - bdata[i] = eval(estr) - else: - bdata[i] = pos.pdata.ix[tdate][weightby] - if invert: - bweights = (2 * bdata.mean() - bdata) / sum(bdata) - else: - bweights = bdata / sum(bdata) - # rebalance - for i, pos in enumerate(positions): - multiplier = pos.multiplier - bdelta = bweights[i] * pvalue - pos.value - cp = pos.pdata.ix[tdate]['close'] - tradesize = math.trunc(bdelta / cp) - ntv = abs(tradesize) * cp * multiplier - if tradesize > 0: - order = Orders.le - if tradesize < 0: - order = Orders.se - exec_trade(p, pos.name, order, tradesize, cp, tdate) - p.cash = currentcash + bdelta - ntv - return p - - -# -# Function kick_out -# - -def kick_out(p, tdate): - r"""Trim the portfolio based on filter criteria. - - To reduce a portfolio's positions, AlphaPy can rank the - positions on some criterion, such as open profit or net - return. On a periodic basis, the worst performers can be - culled from the portfolio. - - Parameters - ---------- - p : alphapy.Portfolio - The portfolio for reducing positions. - tdate : datetime - The date to trim the portfolio positions. - - Returns - ------- - p : alphapy.Portfolio - The reduced portfolio. - - Notes - ----- - - .. warning:: The portfolio management functions ``kick_out``, - ``balance``, and ``stop_loss`` are not part of the - main **StockStream** pipeline, and thus have not been - thoroughly tested. Feel free to exercise the code and - report any issues. - - """ - positions = p.positions - maxpos = p.maxpos - numpos = len(positions) - kovalue = np.zeros(numpos) - koby = p.koby - if not koby: - koby = 'profit' - if koby[0] == "-": - descending = True - koby = koby[1:] - else: - descending = False - attrs = filter(lambda aname: not aname.startswith('_'), dir(positions[0])) - for i, pos in enumerate(positions): - if koby in attrs: - estr = '.'.join('pos', koby) - kovalue[i] = eval(estr) - else: - kovalue[i] = pos.pdata.ix[tdate][koby] - koorder = np.argsort(np.argsort(kovalues)) - if descending: - koorder = [i for i in reversed(koorder)] - if numpos >= maxpos: - freepos = numpos - maxpos + p.kopos - # close the top freepos positions - if freepos > 0: - for i in range(freepos): - p = close_position(p, positions[koorder[i]], tdate) - return p - - -# -# Function stop_loss -# - -def stop_loss(p, tdate): - r"""Trim the portfolio based on stop-loss criteria. - - Parameters - ---------- - p : alphapy.Portfolio - The portfolio for reducing positions based on ``maxloss``. - tdate : datetime - The date to trim any underperforming positions. - - Returns - ------- - p : alphapy.Portfolio - The reduced portfolio. - - Notes - ----- - - .. warning:: The portfolio management functions ``stop_loss``, - ``balance``, and ``kick_out`` are not part of the - main **StockStream** pipeline, and thus have not been - thoroughly tested. Feel free to exercise the code and - report any issues. - - """ - positions = p.positions - maxloss = p.maxloss - for key in positions: - pos = positions[key] - nr = pos.netreturn - if nr <= -maxloss: - p = close_position(p, pos, tdate) - return p - - -# -# Function valuate_portfolio -# - -def valuate_portfolio(p, tdate): - r"""Value the portfolio based on the current positions. - - Parameters - ---------- - p : alphapy.Portfolio - Portfolio for calculating profit and return. - tdate : datetime - The date of valuation. - - Returns - ------- - p : alphapy.Portfolio - Portfolio with the new valuation. - - """ - positions = p.positions - poslen = len(positions) - vpos = [0] * poslen - p.weights = [0] * poslen - posenum = enumerate(positions) - # save the current portfolio value - prev_value = p.value - # compute the total portfolio value - value = p.cash - for i, key in posenum: - pos = positions[key] - pos = valuate_position(pos, tdate) - vpos[i] = pos.value - value = value + vpos[i] - p.value = value - # now compute the weights - for i, key in posenum: - p.weights[i] = vpos[i] / p.value - # update portfolio stats - p.netprofit = p.value - prev_value - p.netreturn = p.value / prev_value - 1.0 - p.totalprofit = p.value - p.startcap - p.totalreturn = p.value / p.startcap - 1.0 - return p - - -# -# Function allocate_trade -# - -def allocate_trade(p, pos, trade): - r"""Determine the trade allocation for a given portfolio. - - Parameters - ---------- - p : alphapy.Portfolio - Portfolio that will hold the new position. - pos : alphapy.Position - Position to update. - trade : alphapy.Trade - The proposed trade. - - Returns - ------- - allocation : float - The trade size that can be allocated for the portfolio. - - """ - cash = p.cash - margin = p.margin - mincash = p.mincash - restricted = p.restricted - if restricted: - kick_out(p, trade.tdate) - stop_loss(p, trade.tdate) - multiplier = pos.multiplier - qpold = pos.quantity - qtrade = trade.quantity - qpnew = qpold + qtrade - allocation = abs(qpnew) - abs(qpold) - addedvalue = trade.price * multiplier * abs(allocation) - if restricted: - cashreserve = mincash * cash - freemargin = (cash - cashreserve) / margin - if addedvalue > freemargin: - logger.info("Required free margin: %d < added value: %d", - freemargin, addedvalue) - allocation = 0 - else: - freecash = cash - addedvalue - if freecash < 0: - p.cash = cash + freecash - return allocation - - -# -# Function exec_trade -# - -def exec_trade(p, name, order, quantity, price, tdate): - r"""Execute a trade for a portfolio. - - Parameters - ---------- - p : alphapy.Portfolio - Portfolio in which to trade. - name : str - The symbol to trade. - order : alphapy.Orders - Long or short trade for entry or exit. - quantity : int - The quantity for the order. - price : str - The execution price of the trade. - tdate : datetime - The date and time of the trade. - - Returns - ------- - tsize : float - The executed trade size. - - Other Parameters - ---------------- - Frame.frames : dict - Dataframe for the price data. - - """ - # see if the position already exists - if name in p.positions: - pos = p.positions[name] - newpos = False - else: - pos = Position(p, name, tdate) - newpos = True - # check the dynamic position sizing variable - if not p.posby: - tsize = quantity - else: - if order == Orders.le or order == Orders.se: - pf = Frame.frames[frame_name(name, p.space)].df - cv = float(pf.ix[tdate][p.posby]) - tsize = math.trunc((p.value * p.fixedfrac) / cv) - if quantity < 0: - tsize = -tsize - else: - tsize = -pos.quantity - # instantiate and allocate the trade - newtrade = Trade(name, order, tsize, price, tdate) - allocation = allocate_trade(p, pos, newtrade) - if allocation != 0: - # create a new position if necessary - if newpos: - p = add_position(p, name, pos) - p.npos += 1 - # update the portfolio - p = update_portfolio(p, pos, newtrade) - # if net position is zero, then close the position - pflat = pos.quantity == 0 - if pflat: - p = close_position(p, pos, tdate) - p.npos -= 1 - else: - logger.info("Trade Allocation for %s is 0", name) - # return trade size - return tsize - - -# -# Function gen_portfolio -# - -def gen_portfolio(model, system, group, tframe, - startcap=100000, posby='close'): - r"""Create a portfolio from a trades frame. - - Parameters - ---------- - model : alphapy.Model - The model with specifications. - system : str - Name of the system. - group : alphapy.Group - The group of instruments in the portfolio. - tframe : pandas.DataFrame - The input trade list from running the system. - startcap : float - Starting capital. - posby : str - The position sizing column in the price dataframe. - - Returns - ------- - p : alphapy.Portfolio - The generated portfolio. - - Raises - ------ - MemoryError - Could not allocate Portfolio. - - Notes - ----- - - This function also generates the files required for analysis - by the *pyfolio* package: - - * Returns File - * Positions File - * Transactions File - - """ - - logger.info("Creating Portfolio for System %s", system) - - # Unpack the model data. - - directory = model.specs['directory'] - extension = model.specs['extension'] - separator = model.specs['separator'] - - # Create the portfolio. - - gname = group.name - gspace = group.space - gmembers = group.members - ff = 1.0 / len(gmembers) - - p = Portfolio(gname, - system, - gspace, - startcap = startcap, - posby = posby, - restricted = False, - fixedfrac = ff) - if not p: - raise MemoryError("Could not allocate Portfolio") - - # Build pyfolio data from the trades frame. - - start = tframe.index[0] - end = tframe.index[-1] - trange = np.unique(tframe.index.map(lambda x: x.date().strftime('%Y-%m-%d'))).tolist() - drange = date_range(start, end).map(lambda x: x.date().strftime('%Y-%m-%d')) - - # Initialize return, position, and transaction data. - - rs = [] - pcols = list(gmembers) - pcols.extend(['cash']) - pf = DataFrame(index=drange, columns=pcols).fillna(0.0) - ts = [] - - # Iterate through the date range, updating the portfolio. - for d in drange: - # process today's trades - if d in trange: - trades = tframe.ix[d] - if isinstance(trades, Series): - trades = DataFrame(trades).transpose() - for t in trades.iterrows(): - tdate = t[0] - row = t[1] - tsize = exec_trade(p, row['name'], row['order'], row['quantity'], row['price'], tdate) - if tsize != 0: - ts.append((d, [tsize, row['price'], row['name']])) - else: - logger.info("Trade could not be executed for %s", row['name']) - # iterate through current positions - positions = p.positions - pfrow = pf.ix[d] - for key in positions: - pos = positions[key] - if pos.quantity > 0: - value = pos.value - else: - value = -pos.value - pfrow[pos.name] = value - pfrow['cash'] = p.cash - # update the portfolio returns - p = valuate_portfolio(p, d) - rs.append((d, [p.netreturn])) - - # Create systems directory path - - system_dir = SSEP.join([directory, 'systems']) - - # Create and record the returns frame for this system. - - logger.info("Recording Returns Frame") - rspace = Space(system, 'returns', gspace.fractal) - rf = DataFrame.from_items(rs, orient='index', columns=['return']) - rfname = frame_name(gname, rspace) - write_frame(rf, system_dir, rfname, extension, separator, - index=True, index_label='date') - del rspace - - # Record the positions frame for this system. - - logger.info("Recording Positions Frame") - pspace = Space(system, 'positions', gspace.fractal) - pfname = frame_name(gname, pspace) - write_frame(pf, system_dir, pfname, extension, separator, - index=True, index_label='date') - del pspace - - # Create and record the transactions frame for this system. - - logger.info("Recording Transactions Frame") - tspace = Space(system, 'transactions', gspace.fractal) - tf = DataFrame.from_items(ts, orient='index', columns=['amount', 'price', 'symbol']) - tfname = frame_name(gname, tspace) - write_frame(tf, system_dir, tfname, extension, separator, - index=True, index_label='date') - del tspace - - # Return the portfolio. - return p diff --git a/alphapy/sport_flow.py.bak b/alphapy/sport_flow.py.bak deleted file mode 100644 index 610a6c8..0000000 --- a/alphapy/sport_flow.py.bak +++ /dev/null @@ -1,914 +0,0 @@ -################################################################################ -# -# Package : AlphaPy -# Module : sport_flow -# Created : July 11, 2013 -# -# Copyright 2017 ScottFree Analytics LLC -# Mark Conway & Robert D. Scott II -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. -# -################################################################################ - - -# -# Imports -# - -print(__doc__) - -from alphapy.__main__ import main_pipeline -from alphapy.frame import read_frame -from alphapy.frame import write_frame -from alphapy.globals import ModelType -from alphapy.globals import Partition, datasets -from alphapy.globals import PSEP, SSEP, USEP -from alphapy.globals import WILDCARD -from alphapy.model import get_model_config -from alphapy.model import Model -from alphapy.space import Space -from alphapy.utilities import valid_date - -import argparse -import datetime -from itertools import groupby -import logging -import math -import numpy as np -import os -import pandas as pd -import yaml - - -# -# Initialize logger -# - -logger = logging.getLogger(__name__) - - -# -# Sports Fields -# -# The following fields are repeated for: -# 1. 'home' -# 2. 'away' -# 3. 'delta' -# -# Note that [Target]s will not be merged into the Game table; -# these targets will be predictors in the Game table that are -# generated after each game result. All of the fields below -# are predictors and are generated a priori, i.e., we calculate -# deltas from the last previously played game for each team and -# these data go into the row for the next game to be played. -# - -sports_dict = {'wins' : int, - 'losses' : int, - 'ties' : int, - 'days_since_first_game' : int, - 'days_since_previous_game' : int, - 'won_on_points' : bool, - 'lost_on_points' : bool, - 'won_on_spread' : bool, - 'lost_on_spread' : bool, - 'point_win_streak' : int, - 'point_loss_streak' : int, - 'point_margin_game' : int, - 'point_margin_season' : int, - 'point_margin_season_avg' : float, - 'point_margin_streak' : int, - 'point_margin_streak_avg' : float, - 'point_margin_ngames' : int, - 'point_margin_ngames_avg' : float, - 'cover_win_streak' : int, - 'cover_loss_streak' : int, - 'cover_margin_game' : float, - 'cover_margin_season' : float, - 'cover_margin_season_avg' : float, - 'cover_margin_streak' : float, - 'cover_margin_streak_avg' : float, - 'cover_margin_ngames' : float, - 'cover_margin_ngames_avg' : float, - 'total_points' : int, - 'overunder_margin' : float, - 'over' : bool, - 'under' : bool, - 'over_streak' : int, - 'under_streak' : int, - 'overunder_season' : float, - 'overunder_season_avg' : float, - 'overunder_streak' : float, - 'overunder_streak_avg' : float, - 'overunder_ngames' : float, - 'overunder_ngames_avg' : float} - - -# -# These are the leaders. Generally, we try to predict one of these -# variables as the target and lag the remaining ones. -# - -game_dict = {'point_margin_game' : int, - 'won_on_points' : bool, - 'lost_on_points' : bool, - 'cover_margin_game' : float, - 'won_on_spread' : bool, - 'lost_on_spread' : bool, - 'overunder_margin' : float, - 'over' : bool, - 'under' : bool} - - -# -# Function get_sport_config -# - -def get_sport_config(): - r"""Read the configuration file for SportFlow. - - Parameters - ---------- - None : None - - Returns - ------- - specs : dict - The parameters for controlling SportFlow. - - """ - - # Read the configuration file - - full_path = SSEP.join(['.', 'config', 'sport.yml']) - with open(full_path, 'r') as ymlfile: - cfg = yaml.load(ymlfile) - - # Store configuration parameters in dictionary - - specs = {} - - # Section: sport - - specs['league'] = cfg['sport']['league'] - specs['points_max'] = cfg['sport']['points_max'] - specs['points_min'] = cfg['sport']['points_min'] - specs['random_scoring'] = cfg['sport']['random_scoring'] - specs['rolling_window'] = cfg['sport']['rolling_window'] - specs['seasons'] = cfg['sport']['seasons'] - - # Log the sports parameters - - logger.info('SPORT PARAMETERS:') - logger.info('league = %s', specs['league']) - logger.info('points_max = %d', specs['points_max']) - logger.info('points_min = %d', specs['points_min']) - logger.info('random_scoring = %r', specs['random_scoring']) - logger.info('rolling_window = %d', specs['rolling_window']) - logger.info('seasons = %s', specs['seasons']) - - # Game Specifications - return specs - - -# -# Function get_point_margin -# - -def get_point_margin(row, score, opponent_score): - r"""Get the point margin for a game. - - Parameters - ---------- - row : pandas.Series - The row of a game. - score : int - The score for one team. - opponent_score : int - The score for the other team. - - Returns - ------- - point_margin : int - The resulting point margin (0 if NaN). - - """ - point_margin = 0 - nans = math.isnan(row[score]) or math.isnan(row[opponent_score]) - if not nans: - point_margin = row[score] - row[opponent_score] - return point_margin - - -# -# Function get_wins -# - -def get_wins(point_margin): - r"""Determine a win based on the point margin. - - Parameters - ---------- - point_margin : int - The point margin can be positive, zero, or negative. - - Returns - ------- - won : int - If the point margin is greater than 0, return 1, else 0. - - """ - won = 1 if point_margin > 0 else 0 - return won - - -# -# Function get_losses -# - -def get_losses(point_margin): - r"""Determine a loss based on the point margin. - - Parameters - ---------- - point_margin : int - The point margin can be positive, zero, or negative. - - Returns - ------- - lost : int - If the point margin is less than 0, return 1, else 0. - - """ - lost = 1 if point_margin < 0 else 0 - return lost - - -# -# Function get_ties -# - -def get_ties(point_margin): - r"""Determine a tie based on the point margin. - - Parameters - ---------- - point_margin : int - The point margin can be positive, zero, or negative. - - Returns - ------- - tied : int - If the point margin is equal to 0, return 1, else 0. - - """ - tied = 1 if point_margin == 0 else 0 - return tied - - -# -# Function get_day_offset -# - -def get_day_offset(date_vector): - r"""Compute the day offsets between games. - - Parameters - ---------- - date_vector : pandas.Series - The date column. - - Returns - ------- - day_offset : pandas.Series - A vector of day offsets between adjacent dates. - - """ - dv = pd.to_datetime(date_vector) - offsets = pd.to_datetime(dv) - pd.to_datetime(dv[0]) - day_offset = offsets.astype('timedelta64[D]').astype(int) - return day_offset - - -# -# Function get_series_diff -# - -def get_series_diff(series): - r"""Perform the difference operation on a series. - - Parameters - ---------- - series : pandas.Series - The series for the ``diff`` operation. - - Returns - ------- - new_series : pandas.Series - The differenced series. - - """ - new_series = pd.Series(len(series)) - new_series = series.diff() - new_series[0] = 0 - return new_series - - -# -# Function get_streak -# - -def get_streak(series, start_index, window): - r"""Calculate the current streak. - - Parameters - ---------- - series : pandas.Series - A Boolean series for calculating streaks. - start_index : int - The offset of the series to start counting. - window : int - The period over which to count. - - Returns - ------- - streak : int - The count value for the current streak. - - """ - if window <= 0: - window = len(series) - i = start_index - streak = 0 - while i >= 0 and (start_index-i+1) < window and series[i]: - streak += 1 - i -= 1 - return streak - - -# -# Function add_features -# - -def add_features(frame, fdict, flen, prefix=''): - r"""Add new features to a dataframe with the specified dictionary. - - Parameters - ---------- - frame : pandas.DataFrame - The dataframe to extend with new features defined by ``fdict``. - fdict : dict - A dictionary of column names (key) and data types (value). - flen : int - Length of ``frame``. - prefix : str, optional - Prepend all columns with a prefix. - - Returns - ------- - frame : pandas.DataFrame - The dataframe with the added features. - - """ - # generate sequences - seqint = [0] * flen - seqfloat = [0.0] * flen - seqbool = [False] * flen - # initialize new fields in frame - for key, value in fdict.items(): - newkey = key - if prefix: - newkey = PSEP.join([prefix, newkey]) - if value == int: - frame[newkey] = pd.Series(seqint) - elif value == float: - frame[newkey] = pd.Series(seqfloat) - elif value == bool: - frame[newkey] = pd.Series(seqbool) - else: - raise ValueError("Type to generate feature series not found") - return frame - - -# -# Function generate_team_frame -# - -def generate_team_frame(team, tf, home_team, away_team, window): - r"""Calculate statistics for each team. - - Parameters - ---------- - team : str - The abbreviation for the team. - tf : pandas.DataFrame - The initial team frame. - home_team : str - Label for the home team. - away_team : str - Label for the away team. - window : int - The value for the rolling window to calculate means and sums. - - Returns - ------- - tf : pandas.DataFrame - The completed team frame. - - """ - # Initialize new features - tf = add_features(tf, sports_dict, len(tf)) - # Daily Offsets - tf['days_since_first_game'] = get_day_offset(tf['date']) - tf['days_since_previous_game'] = get_series_diff(tf['days_since_first_game']) - # Team Loop - for index, row in tf.iterrows(): - if team == row[home_team]: - tf['point_margin_game'].at[index] = get_point_margin(row, 'home.score', 'away.score') - line = row['line'] - elif team == row[away_team]: - tf['point_margin_game'].at[index] = get_point_margin(row, 'away.score', 'home.score') - line = -row['line'] - else: - raise KeyError("Team not found in Team Frame") - if index == 0: - tf['wins'].at[index] = get_wins(tf['point_margin_game'].at[index]) - tf['losses'].at[index] = get_losses(tf['point_margin_game'].at[index]) - tf['ties'].at[index] = get_ties(tf['point_margin_game'].at[index]) - else: - tf['wins'].at[index] = tf['wins'].at[index-1] + get_wins(tf['point_margin_game'].at[index]) - tf['losses'].at[index] = tf['losses'].at[index-1] + get_losses(tf['point_margin_game'].at[index]) - tf['ties'].at[index] = tf['ties'].at[index-1] + get_ties(tf['point_margin_game'].at[index]) - tf['won_on_points'].at[index] = True if tf['point_margin_game'].at[index] > 0 else False - tf['lost_on_points'].at[index] = True if tf['point_margin_game'].at[index] < 0 else False - tf['cover_margin_game'].at[index] = tf['point_margin_game'].at[index] + line - tf['won_on_spread'].at[index] = True if tf['cover_margin_game'].at[index] > 0 else False - tf['lost_on_spread'].at[index] = True if tf['cover_margin_game'].at[index] <= 0 else False - nans = math.isnan(row['home.score']) or math.isnan(row['away.score']) - if not nans: - tf['total_points'].at[index] = row['home.score'] + row['away.score'] - nans = math.isnan(row['over_under']) - if not nans: - tf['overunder_margin'].at[index] = tf['total_points'].at[index] - row['over_under'] - tf['over'].at[index] = True if tf['overunder_margin'].at[index] > 0 else False - tf['under'].at[index] = True if tf['overunder_margin'].at[index] < 0 else False - tf['point_win_streak'].at[index] = get_streak(tf['won_on_points'], index, 0) - tf['point_loss_streak'].at[index] = get_streak(tf['lost_on_points'], index, 0) - tf['cover_win_streak'].at[index] = get_streak(tf['won_on_spread'], index, 0) - tf['cover_loss_streak'].at[index] = get_streak(tf['lost_on_spread'], index, 0) - tf['over_streak'].at[index] = get_streak(tf['over'], index, 0) - tf['under_streak'].at[index] = get_streak(tf['under'], index, 0) - # Handle the streaks - if tf['point_win_streak'].at[index] > 0: - streak = tf['point_win_streak'].at[index] - elif tf['point_loss_streak'].at[index] > 0: - streak = tf['point_loss_streak'].at[index] - else: - streak = 1 - tf['point_margin_streak'].at[index] = tf['point_margin_game'][index-streak+1:index+1].sum() - tf['point_margin_streak_avg'].at[index] = tf['point_margin_game'][index-streak+1:index+1].mean() - if tf['cover_win_streak'].at[index] > 0: - streak = tf['cover_win_streak'].at[index] - elif tf['cover_loss_streak'].at[index] > 0: - streak = tf['cover_loss_streak'].at[index] - else: - streak = 1 - tf['cover_margin_streak'].at[index] = tf['cover_margin_game'][index-streak+1:index+1].sum() - tf['cover_margin_streak_avg'].at[index] = tf['cover_margin_game'][index-streak+1:index+1].mean() - if tf['over_streak'].at[index] > 0: - streak = tf['over_streak'].at[index] - elif tf['under_streak'].at[index] > 0: - streak = tf['under_streak'].at[index] - else: - streak = 1 - tf['overunder_streak'].at[index] = tf['overunder_margin'][index-streak+1:index+1].sum() - tf['overunder_streak_avg'].at[index] = tf['overunder_margin'][index-streak+1:index+1].mean() - # Rolling and Expanding Variables - tf['point_margin_season'] = tf['point_margin_game'].cumsum() - tf['point_margin_season_avg'] = tf['point_margin_game'].expanding().mean() - tf['point_margin_ngames'] = tf['point_margin_game'].rolling(window=window, min_periods=1).sum() - tf['point_margin_ngames_avg'] = tf['point_margin_game'].rolling(window=window, min_periods=1).mean() - tf['cover_margin_season'] = tf['cover_margin_game'].cumsum() - tf['cover_margin_season_avg'] = tf['cover_margin_game'].expanding().mean() - tf['cover_margin_ngames'] = tf['cover_margin_game'].rolling(window=window, min_periods=1).sum() - tf['cover_margin_ngames_avg'] = tf['cover_margin_game'].rolling(window=window, min_periods=1).mean() - tf['overunder_season'] = tf['overunder_margin'].cumsum() - tf['overunder_season_avg'] = tf['overunder_margin'].expanding().mean() - tf['overunder_ngames'] = tf['overunder_margin'].rolling(window=window, min_periods=1).sum() - tf['overunder_ngames_avg'] = tf['overunder_margin'].rolling(window=window, min_periods=1).mean() - return tf - - -# -# Function get_team_frame -# - -def get_team_frame(game_frame, team, home, away): - r"""Calculate statistics for each team. - - Parameters - ---------- - game_frame : pandas.DataFrame - The game frame for a given season. - team : str - The team abbreviation. - home : str - The label of the home team column. - away : int - The label of the away team column. - - Returns - ------- - team_frame : pandas.DataFrame - The extracted team frame. - - """ - team_frame = game_frame[(game_frame[home] == team) | (game_frame[away] == team)] - return team_frame - - -# -# Function insert_model_data -# - -def insert_model_data(mf, mpos, mdict, tf, tpos, prefix): - r"""Insert a row from the team frame into the model frame. - - Parameters - ---------- - mf : pandas.DataFrame - The model frame for a single season. - mpos : int - The position in the model frame where to insert the row. - mdict : dict - A dictionary of column names (key) and data types (value). - tf : pandas.DataFrame - The team frame for a season. - tpos : int - The position of the row in the team frame. - prefix : str - The prefix to join with the ``mdict`` key. - - Returns - ------- - mf : pandas.DataFrame - The . - - """ - team_row = tf.iloc[tpos] - for key, value in mdict.items(): - newkey = key - if prefix: - newkey = PSEP.join([prefix, newkey]) - mf.at[mpos, newkey] = team_row[key] - return mf - - -# -# Function generate_delta_data -# - -def generate_delta_data(frame, fdict, prefix1, prefix2): - r"""Subtract two similar columns to get the delta value. - - Parameters - ---------- - frame : pandas.DataFrame - The input model frame. - fdict : dict - A dictionary of column names (key) and data types (value). - prefix1 : str - The prefix of the first team. - prefix2 : str - The prefix of the second team. - - Returns - ------- - frame : pandas.DataFrame - The completed dataframe with the delta data. - - """ - for key, value in fdict.items(): - newkey = PSEP.join(['delta', key]) - key1 = PSEP.join([prefix1, key]) - key2 = PSEP.join([prefix2, key]) - frame[newkey] = frame[key1] - frame[key2] - return frame - - -# -# Function main -# - -def main(args=None): - r"""The main program for SportFlow. - - Notes - ----- - (1) Initialize logging. - (2) Parse the command line arguments. - (3) Get the game configuration. - (4) Get the model configuration. - (5) Generate game frames for each season. - (6) Create statistics for each team. - (7) Merge the team frames into the final model frame. - (8) Run the AlphaPy pipeline. - - Raises - ------ - ValueError - Training date must be before prediction date. - - """ - - # Logging - - logging.basicConfig(format="[%(asctime)s] %(levelname)s\t%(message)s", - filename="sport_flow.log", filemode='a', level=logging.DEBUG, - datefmt='%m/%d/%y %H:%M:%S') - formatter = logging.Formatter("[%(asctime)s] %(levelname)s\t%(message)s", - datefmt='%m/%d/%y %H:%M:%S') - console = logging.StreamHandler() - console.setFormatter(formatter) - console.setLevel(logging.INFO) - logging.getLogger().addHandler(console) - - logger = logging.getLogger(__name__) - - # Start the pipeline - - logger.info('*'*80) - logger.info("SportFlow Start") - logger.info('*'*80) - - # Argument Parsing - - parser = argparse.ArgumentParser(description="SportFlow Parser") - parser.add_argument('--pdate', dest='predict_date', - help="prediction date is in the format: YYYY-MM-DD", - required=False, type=valid_date) - parser.add_argument('--tdate', dest='train_date', - help="training date is in the format: YYYY-MM-DD", - required=False, type=valid_date) - parser.add_mutually_exclusive_group(required=False) - parser.add_argument('--predict', dest='predict_mode', action='store_true') - parser.add_argument('--train', dest='predict_mode', action='store_false') - parser.set_defaults(predict_mode=False) - args = parser.parse_args() - - # Set train and predict dates - - if args.train_date: - train_date = args.train_date - else: - train_date = pd.datetime(1900, 1, 1).strftime("%Y-%m-%d") - - if args.predict_date: - predict_date = args.predict_date - else: - predict_date = datetime.date.today().strftime("%Y-%m-%d") - - # Verify that the dates are in sequence. - - if train_date >= predict_date: - raise ValueError("Training date must be before prediction date") - else: - logger.info("Training Date: %s", train_date) - logger.info("Prediction Date: %s", predict_date) - - # Read game configuration file - - sport_specs = get_sport_config() - - # Section: game - - league = sport_specs['league'] - points_max = sport_specs['points_max'] - points_min = sport_specs['points_min'] - random_scoring = sport_specs['random_scoring'] - seasons = sport_specs['seasons'] - window = sport_specs['rolling_window'] - - # Read model configuration file - - specs = get_model_config() - - # Add command line arguments to model specifications - - specs['predict_mode'] = args.predict_mode - specs['predict_date'] = args.predict_date - specs['train_date'] = args.train_date - - # Unpack model arguments - - directory = specs['directory'] - target = specs['target'] - - # Create directories if necessary - - output_dirs = ['config', 'data', 'input', 'model', 'output', 'plots'] - for od in output_dirs: - output_dir = SSEP.join([directory, od]) - if not os.path.exists(output_dir): - logger.info("Creating directory %s", output_dir) - os.makedirs(output_dir) - - # Create the game scores space - space = Space('game', 'scores', '1g') - - # - # Derived Variables - # - - series = space.schema - team1_prefix = 'home' - team2_prefix = 'away' - home_team = PSEP.join([team1_prefix, 'team']) - away_team = PSEP.join([team2_prefix, 'team']) - - # - # Read in the game frame. This is the feature generation phase. - # - - logger.info("Reading Game Data") - - data_dir = SSEP.join([directory, 'data']) - file_base = USEP.join([league, space.subject, space.schema, space.fractal]) - df = read_frame(data_dir, file_base, specs['extension'], specs['separator']) - logger.info("Total Game Records: %d", df.shape[0]) - - # - # Locate any rows with null values - # - - null_rows = df.isnull().any(axis=1) - null_indices = [i for i, val in enumerate(null_rows.tolist()) if val == True] - for i in null_indices: - logger.info("Null Record: %d on Date: %s", i, df.date[i]) - - # - # Run the game pipeline on a seasonal loop - # - - if not seasons: - # run model on all seasons - seasons = df['season'].unique().tolist() - - # - # Initialize the final frame - # - - ff = pd.DataFrame() - - # - # Iterate through each season of the game frame - # - - for season in seasons: - - # Generate a frame for each season - - gf = df[df['season'] == season] - gf = gf.reset_index(level=0) - - # Generate derived variables for the game frame - - total_games = gf.shape[0] - if random_scoring: - gf['home.score'] = np.random.randint(points_min, points_max, total_games) - gf['away.score'] = np.random.randint(points_min, points_max, total_games) - gf['total_points'] = gf['home.score'] + gf['away.score'] - - gf = add_features(gf, game_dict, gf.shape[0]) - for index, row in gf.iterrows(): - gf['point_margin_game'].at[index] = get_point_margin(row, 'home.score', 'away.score') - gf['won_on_points'].at[index] = True if gf['point_margin_game'].at[index] > 0 else False - gf['lost_on_points'].at[index] = True if gf['point_margin_game'].at[index] < 0 else False - gf['cover_margin_game'].at[index] = gf['point_margin_game'].at[index] + row['line'] - gf['won_on_spread'].at[index] = True if gf['cover_margin_game'].at[index] > 0 else False - gf['lost_on_spread'].at[index] = True if gf['cover_margin_game'].at[index] <= 0 else False - gf['overunder_margin'].at[index] = gf['total_points'].at[index] - row['over_under'] - gf['over'].at[index] = True if gf['overunder_margin'].at[index] > 0 else False - gf['under'].at[index] = True if gf['overunder_margin'].at[index] < 0 else False - - # Generate each team frame - - team_frames = {} - teams = gf.groupby([home_team]) - for team, data in teams: - team_frame = USEP.join([league, team.lower(), series, str(season)]) - logger.info("Generating team frame: %s", team_frame) - tf = get_team_frame(gf, team, home_team, away_team) - tf = tf.reset_index(level=0) - tf = generate_team_frame(team, tf, home_team, away_team, window) - team_frames[team_frame] = tf - - # Create the model frame, initializing the home and away frames - - mdict = {k:v for (k,v) in sports_dict.items() if v != bool} - team1_frame = pd.DataFrame() - team1_frame = add_features(team1_frame, mdict, gf.shape[0], prefix=team1_prefix) - team2_frame = pd.DataFrame() - team2_frame = add_features(team2_frame, mdict, gf.shape[0], prefix=team2_prefix) - frames = [gf, team1_frame, team2_frame] - mf = pd.concat(frames, axis=1) - - # Loop through each team frame, inserting data into the model frame row - # get index+1 [if valid] - # determine if team is home or away to get prefix - # try: np.where((gf[home_team] == 'PHI') & (gf['date'] == '09/07/14'))[0][0] - # Assign team frame fields to respective model frame fields: set gf.at(pos, field) - - for team, data in teams: - team_frame = USEP.join([league, team.lower(), series, str(season)]) - logger.info("Merging team frame %s into model frame", team_frame) - tf = team_frames[team_frame] - for index in range(0, tf.shape[0]-1): - gindex = index + 1 - model_row = tf.iloc[gindex] - key_date = model_row['date'] - at_home = False - if team == model_row[home_team]: - at_home = True - key_team = model_row[home_team] - elif team == model_row[away_team]: - key_team = model_row[away_team] - else: - raise KeyError("Team %s not found in Team Frame" % team) - try: - if at_home: - mpos = np.where((mf[home_team] == key_team) & (mf['date'] == key_date))[0][0] - else: - mpos = np.where((mf[away_team] == key_team) & (mf['date'] == key_date))[0][0] - except: - raise IndexError("Team/Date Key not found in Model Frame") - # print team, gindex, mpos - # insert team data into model row - mf = insert_model_data(mf, mpos, mdict, tf, index, team1_prefix if at_home else team2_prefix) - - # Compute delta data 'home' - 'away' - mf = generate_delta_data(mf, mdict, team1_prefix, team2_prefix) - - # Append this to final frame - frames = [ff, mf] - ff = pd.concat(frames) - - # Write out dataframes - - input_dir = SSEP.join([directory, 'input']) - if args.predict_mode: - new_predict_frame = ff.loc[ff.date >= predict_date] - if len(new_predict_frame) <= 1: - raise ValueError("Prediction frame has length 1 or less") - # rewrite with all the features to the train and test files - logger.info("Saving prediction frame") - write_frame(new_predict_frame, input_dir, datasets[Partition.predict], - specs['extension'], specs['separator']) - else: - # split data into training and test data - new_train_frame = ff.loc[(ff.date >= train_date) & (ff.date < predict_date)] - if len(new_train_frame) <= 1: - raise ValueError("Training frame has length 1 or less") - new_test_frame = ff.loc[ff.date >= predict_date] - if len(new_test_frame) <= 1: - raise ValueError("Testing frame has length 1 or less") - # rewrite with all the features to the train and test files - logger.info("Saving training frame") - write_frame(new_train_frame, input_dir, datasets[Partition.train], - specs['extension'], specs['separator']) - logger.info("Saving testing frame") - write_frame(new_test_frame, input_dir, datasets[Partition.test], - specs['extension'], specs['separator']) - - # Create the model from specs - - logger.info("Running Model") - model = Model(specs) - - # Run the pipeline - model = main_pipeline(model) - - # Complete the pipeline - - logger.info('*'*80) - logger.info("SportFlow End") - logger.info('*'*80) - - -# -# MAIN PROGRAM -# - -if __name__ == "__main__": - main() From 287cea308d799e5e65c6ff00752c52b2bd633e5b Mon Sep 17 00:00:00 2001 From: Mark Conway Date: Tue, 23 May 2017 09:19:20 -0400 Subject: [PATCH 004/129] remove level=0 for reset_index remove level=0 for reset_index --- alphapy/sport_flow.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/alphapy/sport_flow.py b/alphapy/sport_flow.py index e6d74c4..f8af0d3 100644 --- a/alphapy/sport_flow.py +++ b/alphapy/sport_flow.py @@ -780,7 +780,7 @@ def main(args=None): # Generate a frame for each season gf = df[df['season'] == season] - gf = gf.reset_index(level=0) + gf = gf.reset_index() # Generate derived variables for the game frame @@ -810,7 +810,7 @@ def main(args=None): team_frame = USEP.join([league, team.lower(), series, str(season)]) logger.info("Generating team frame: %s", team_frame) tf = get_team_frame(gf, team, home_team, away_team) - tf = tf.reset_index(level=0) + tf = tf.reset_index() tf = generate_team_frame(team, tf, home_team, away_team, window) team_frames[team_frame] = tf From 69958c213e26ee5f6c20b49f9806a66d15c85b4c Mon Sep 17 00:00:00 2001 From: Mark Conway Date: Tue, 30 May 2017 08:39:48 -0400 Subject: [PATCH 005/129] update build for 2.01 update build for 2.01 --- docs/conf.py | 4 ++-- setup.py | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/docs/conf.py b/docs/conf.py index ec841cf..92394de 100644 --- a/docs/conf.py +++ b/docs/conf.py @@ -62,9 +62,9 @@ # built documents. # # The short X.Y version. -version = '2.0' +version = '2.01' # The full version, including alpha/beta/rc tags. -release = '2.0' +release = '2.01' # The language for content autogenerated by Sphinx. Refer to documentation # for a list of supported languages. diff --git a/setup.py b/setup.py index 9b75c6e..c78c5a0 100644 --- a/setup.py +++ b/setup.py @@ -9,8 +9,8 @@ MAINTAINER = 'ScottFree LLC [Robert D. Scott II, Mark Conway]' MAINTAINER_EMAIL = 'mark.conway@scottfreellc.com' URL = "https://github.com/ScottFreeLLC/AlphaPy" -LICENSE = "Apache License, Version 2.0" -VERSION = "2.0" +LICENSE = "Apache License, Version 2.01" +VERSION = "2.01" classifiers = ['Development Status :: 4 - Beta', 'Programming Language :: Python', From 4941b903e8fa5389734a4b343ad1bc400125b69a Mon Sep 17 00:00:00 2001 From: Mark Conway Date: Tue, 30 May 2017 08:42:09 -0400 Subject: [PATCH 006/129] version number --- docs/conf.py | 4 ++-- setup.py | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/docs/conf.py b/docs/conf.py index 92394de..1d66e86 100644 --- a/docs/conf.py +++ b/docs/conf.py @@ -62,9 +62,9 @@ # built documents. # # The short X.Y version. -version = '2.01' +version = '2.0.1' # The full version, including alpha/beta/rc tags. -release = '2.01' +release = '2.0.1' # The language for content autogenerated by Sphinx. Refer to documentation # for a list of supported languages. diff --git a/setup.py b/setup.py index c78c5a0..b36a361 100644 --- a/setup.py +++ b/setup.py @@ -9,8 +9,8 @@ MAINTAINER = 'ScottFree LLC [Robert D. Scott II, Mark Conway]' MAINTAINER_EMAIL = 'mark.conway@scottfreellc.com' URL = "https://github.com/ScottFreeLLC/AlphaPy" -LICENSE = "Apache License, Version 2.01" -VERSION = "2.01" +LICENSE = "Apache License, Version 2.0.1" +VERSION = "2.0.1" classifiers = ['Development Status :: 4 - Beta', 'Programming Language :: Python', From 7c6a7e43d1dbcdda0d164c650676745cd45b2cf4 Mon Sep 17 00:00:00 2001 From: Mark Conway Date: Sun, 25 Jun 2017 10:45:09 -0400 Subject: [PATCH 007/129] change default schema to google change default schema to google --- .gitignore | 12 ++++++++++++ alphapy/examples/Trading Model/config/market.yml | 2 +- alphapy/examples/Trading System/config/market.yml | 2 +- 3 files changed, 14 insertions(+), 2 deletions(-) diff --git a/.gitignore b/.gitignore index de6fafb..613ebe6 100644 --- a/.gitignore +++ b/.gitignore @@ -12,3 +12,15 @@ *.whl *.gz + +.idea/encodings.xml + +.idea/inspectionProfiles/profiles_settings.xml + +.idea/misc.xml + +.idea/modules.xml + +.idea/vcs.xml + +.idea/workspace.xml diff --git a/alphapy/examples/Trading Model/config/market.yml b/alphapy/examples/Trading Model/config/market.yml index 7f73975..c7614a1 100644 --- a/alphapy/examples/Trading Model/config/market.yml +++ b/alphapy/examples/Trading Model/config/market.yml @@ -4,7 +4,7 @@ market: fractal : 1d leaders : ['gap', 'gapbadown', 'gapbaup', 'gapdown', 'gapup'] predict_history : 100 - schema : prices + schema : google target_group : test groups: diff --git a/alphapy/examples/Trading System/config/market.yml b/alphapy/examples/Trading System/config/market.yml index 635824e..6991b1f 100644 --- a/alphapy/examples/Trading System/config/market.yml +++ b/alphapy/examples/Trading System/config/market.yml @@ -4,7 +4,7 @@ market: fractal : 1d leaders : [] predict_history : 50 - schema : prices + schema : google target_group : faang system: From 4074585b32bb5b876cf4bb8b7d097f6757bed28e Mon Sep 17 00:00:00 2001 From: Mark Conway Date: Fri, 20 Oct 2017 14:09:37 -0400 Subject: [PATCH 008/129] feature sequencing add sequence-to-sequence input and output features --- alphapy/__main__.py | 40 +++++++++++++++---------- alphapy/analysis.py | 43 +++++++++++++++++++------- alphapy/data.py | 15 ++++++---- alphapy/features.py | 53 +++++++++++++++++++++++++------- alphapy/frame.py | 68 +++++++++++++++++++++++++++++++++++++++++- alphapy/globals.py | 1 + alphapy/market_flow.py | 16 +++++++--- alphapy/model.py | 6 ++-- alphapy/utilities.py | 19 ++++++++++++ setup.py | 6 ++-- 10 files changed, 213 insertions(+), 54 deletions(-) diff --git a/alphapy/__main__.py b/alphapy/__main__.py index 86ab413..ef5fa4b 100644 --- a/alphapy/__main__.py +++ b/alphapy/__main__.py @@ -39,6 +39,7 @@ from alphapy.features import remove_lv_features from alphapy.features import save_features from alphapy.features import select_features +from alphapy.frame import write_frame from alphapy.globals import CSEP, PSEP, SSEP, USEP from alphapy.globals import ModelType from alphapy.globals import Partition, datasets @@ -59,6 +60,7 @@ from alphapy.optimize import rfe_search from alphapy.optimize import rfecv_search from alphapy.plots import generate_plots +from alphapy.utilities import get_datestamp from alphapy.utilities import np_store_data import argparse @@ -106,7 +108,9 @@ def training_pipeline(model): # Unpack the model specifications calibration = model.specs['calibration'] + directory = model.specs['directory'] drop = model.specs['drop'] + extension = model.specs['extension'] feature_selection = model.specs['feature_selection'] grid_search = model.specs['grid_search'] model_type = model.specs['model_type'] @@ -114,6 +118,7 @@ def training_pipeline(model): rfe = model.specs['rfe'] sampling = model.specs['sampling'] scorer = model.specs['scorer'] + separator = model.specs['separator'] target = model.specs['target'] # Get train and test data @@ -128,13 +133,6 @@ def training_pipeline(model): model.test_labels = True model = save_features(model, X_train, X_test, y_train, y_test) - # Drop features - - logger.info("Dropping Features: %s", drop) - X_train = drop_features(X_train, drop) - X_test = drop_features(X_test, drop) - model = save_features(model, X_train, X_test) - # Log feature statistics logger.info("Original Feature Statistics") @@ -161,10 +159,24 @@ def training_pipeline(model): (X_train.shape[1], X_test.shape[1])) # Apply treatments to the feature matrix - all_features = apply_treatments(model, X) - X_train, X_test = np.array_split(all_features, [split_point]) - model = save_features(model, X_train, X_test) + + # Drop features + all_features = drop_features(all_features, drop) + + # Save the train and test files with extracted and dropped features + + datestamp = get_datestamp() + data_dir = SSEP.join([directory, 'input']) + df_train = all_features.iloc[:split_point, :] + df_train = pd.concat([df_train, pd.DataFrame(y_train, columns=[target])], axis=1) + output_file = USEP.join([model.train_file, datestamp]) + write_frame(df_train, data_dir, output_file, extension, separator) + df_test = all_features.iloc[split_point:, :] + if y_test.any(): + df_test = pd.concat([df_test, pd.DataFrame(y_test, columns=[target])], axis=1) + output_file = USEP.join([model.test_file, datestamp]) + write_frame(df_test, data_dir, output_file, extension, separator) # Create crosstabs for any categorical features @@ -315,11 +327,6 @@ def prediction_pipeline(model): # Load feature_map model = load_feature_map(model, directory) - # Drop features - - logger.info("Dropping Features: %s", drop) - X_predict = drop_features(X_predict, drop) - # Log feature statistics logger.info("Feature Statistics") @@ -329,6 +336,9 @@ def prediction_pipeline(model): # Apply treatments to the feature matrix all_features = apply_treatments(model, X_predict) + # Drop features + all_features = drop_features(all_features, drop) + # Create initial features all_features = create_features(model, all_features) diff --git a/alphapy/analysis.py b/alphapy/analysis.py index d893a89..271acb0 100644 --- a/alphapy/analysis.py +++ b/alphapy/analysis.py @@ -28,8 +28,9 @@ from alphapy.__main__ import main_pipeline from alphapy.frame import load_frames +from alphapy.frame import sequence_frame from alphapy.frame import write_frame -from alphapy.globals import SSEP, USEP +from alphapy.globals import SSEP, TAG_ID, USEP from alphapy.utilities import subtract_days from datetime import timedelta @@ -133,7 +134,7 @@ def __str__(self): # Function run_analysis # -def run_analysis(analysis, forecast_period, leaders, +def run_analysis(analysis, lag_period, forecast_period, leaders, predict_history, splits=True): r"""Run an analysis for a given model and group. @@ -147,10 +148,14 @@ def run_analysis(analysis, forecast_period, leaders, ---------- analysis : alphapy.Analysis The analysis to run. + lag_period : int + The number of lagged features for the analysis. forecast_period : int The period for forecasting the target of the analysis. leaders : list The features that are contemporaneous with the target. + predict_history : int + The number of periods required for lookback calculations. splits : bool, optional If ``True``, then the data for each member of the analysis group are in separate files. @@ -185,7 +190,11 @@ def run_analysis(analysis, forecast_period, leaders, train_date = model.specs['train_date'] # Calculate split date + logger.info("Analysis Dates") split_date = subtract_days(predict_date, predict_history) + logger.info("Train Date: %s", train_date) + logger.info("Split Date: %s", split_date) + logger.info("Test Date: %s", predict_date) # Load the data frames data_frames = load_frames(group, directory, extension, separator, splits) @@ -203,20 +212,24 @@ def run_analysis(analysis, forecast_period, leaders, # Subset each individual frame and add to the master frame for df in data_frames: + try: + tag = df[TAG_ID].unique()[0] + except: + tag = 'Unknown' + first_date = df.index[0] last_date = df.index[-1] - # shift the target for the forecast period - if forecast_period > 0: - df[target] = df[target].shift(-forecast_period) - # shift any leading features if necessary - if leaders: - df[leaders] = df[leaders].shift(-1) + logger.info("Analyzing %s from %s to %s", tag, first_date, last_date) + # sequence leaders, laggards, and target(s) + df = sequence_frame(df, target, leaders, lag_period, forecast_period, + exclude_cols=[TAG_ID]) # get frame subsets if predict_mode: new_predict = df.loc[(df.index >= split_date) & (df.index <= last_date)] if len(new_predict) > 0: predict_frame = predict_frame.append(new_predict) else: - logger.info("A prediction frame has zero rows. Check prediction date.") + logger.info("Prediction frame %s has zero rows. Check prediction date.", + tag) else: # split data into train and test new_train = df.loc[(df.index >= train_date) & (df.index < split_date)] @@ -225,12 +238,20 @@ def run_analysis(analysis, forecast_period, leaders, train_frame = train_frame.append(new_train) new_test = df.loc[(df.index >= split_date) & (df.index <= last_date)] if len(new_test) > 0: + # check if target column has NaN values + nan_count = df[target].isnull().sum() + forecast_check = forecast_period - 1 + if nan_count != forecast_check: + logger.info("%s has %d records with NaN targets", tag, nan_count) + # drop records with NaN values in target column new_test = new_test.dropna(subset=[target]) + # append selected records to the test frame test_frame = test_frame.append(new_test) else: - logger.info("A testing frame has zero rows. Check prediction date.") + logger.info("Testing frame %s has zero rows. Check prediction date.", + tag) else: - logger.warning("A training frame has zero rows. Check data source.") + logger.info("Training frame %s has zero rows. Check data source.", tag) # Write out the frames for input into the AlphaPy pipeline diff --git a/alphapy/data.py b/alphapy/data.py index b6e7853..679962f 100644 --- a/alphapy/data.py +++ b/alphapy/data.py @@ -420,12 +420,12 @@ def get_feed_data(group, lookback_period): group : alphapy.Group The group of symbols. lookback_period : int - The number of days of data to retrieve. + The number of periods of data to retrieve. Returns ------- - daily_data : bool - ``True`` if daily data + n_periods : int + The maximum number of periods actually retrieved. """ @@ -442,6 +442,7 @@ def get_feed_data(group, lookback_period): logger.info("Getting Intraday Data (Google 50-day limit)") daily_data = False # Get the data from the relevant feed + n_periods = 0 for item in group.members: logger.info("Getting %s data for last %d days", item, lookback_period) if daily_data: @@ -453,7 +454,11 @@ def get_feed_data(group, lookback_period): newf = Frame(item.lower(), gspace, df) if newf is None: logger.error("Could not allocate Frame for: %s", item) + # calculate maximum number of periods + df_len = len(df) + if df_len > n_periods: + n_periods = df_len else: logger.info("No DataFrame for %s", item) - # Indicate whether or not data is daily - return daily_data + # The number of periods actually retrieved + return n_periods diff --git a/alphapy/features.py b/alphapy/features.py index 9d10737..a70f9c7 100644 --- a/alphapy/features.py +++ b/alphapy/features.py @@ -26,11 +26,13 @@ # Imports # -from alphapy.globals import BSEP, NULLTEXT, PSEP, SSEP, USEP +from alphapy.globals import BSEP, LOFF, NULLTEXT +from alphapy.globals import PSEP, SSEP, USEP from alphapy.globals import Encoders from alphapy.globals import ModelType from alphapy.globals import Scalers from alphapy.market_variables import Variable +from alphapy.market_variables import vparse import category_encoders as ce from importlib import import_module @@ -38,6 +40,7 @@ import logging import math import numpy as np +import os import pandas as pd import re from scipy import sparse @@ -61,6 +64,7 @@ from sklearn.preprocessing import MinMaxScaler from sklearn.preprocessing import PolynomialFeatures from sklearn.preprocessing import StandardScaler +import sys # @@ -424,6 +428,8 @@ def apply_treatment(fname, df, fparams): module = fparams[0] func_name = fparams[1] plist = fparams[2:] + # Append to system path + sys.path.append(os.getcwd()) # Import the external treatment function ext_module = import_module(module) func = getattr(ext_module, func_name) @@ -476,17 +482,34 @@ def apply_treatments(model, X): logger.info("Applying Treatments") all_features = X - for fname in X: - if treatments and fname in treatments: - features = apply_treatment(fname, X, treatments[fname]) - if features is not None: - if features.shape[0] == X.shape[0]: - all_features = pd.concat([all_features, features], axis=1) + if treatments: + for fname in treatments: + # find feature series + fcols = [] + for col in X.columns: + if col.split(LOFF)[0] == fname: + fcols.append(col) + # get lag values + lag_values = [] + for item in fcols: + _, _, _, lag = vparse(item) + lag_values.append(lag) + # apply treatment to the most recent value + if lag_values: + f_latest = fcols[lag_values.index(min(lag_values))] + features = apply_treatment(f_latest, X, treatments[fname]) + if features is not None: + if features.shape[0] == X.shape[0]: + all_features = pd.concat([all_features, features], axis=1) + else: + raise IndexError("The number of treatment rows [%d] must match X [%d]" % + (features.shape[0], X.shape[0])) else: - raise IndexError("The number of treatment rows [%d] must match X [%d]" % - (features.shape[0], X.shape[0])) + logger.info("Could not apply treatment for feature %s", fname) else: - logger.info("Could not apply treatment for feature %s", fname) + logger.info("Feature %s is missing for treatment", fname) + else: + logger.info("No Treatments Specified") logger.info("New Feature Count : %d", all_features.shape[1]) @@ -1575,7 +1598,15 @@ def drop_features(X, drop): The dataframe without the dropped features. """ - X.drop(drop, axis=1, inplace=True, errors='ignore') + drop_cols = [] + for d in drop: + for col in X.columns: + if col.split(LOFF)[0] == d: + drop_cols.append(col) + logger.info("Dropping Features: %s", drop_cols) + logger.info("Original Feature Count : %d", X.shape[1]) + X.drop(drop_cols, axis=1, inplace=True, errors='ignore') + logger.info("Reduced Feature Count : %d", X.shape[1]) return X diff --git a/alphapy/frame.py b/alphapy/frame.py index 6723e5b..106b5d1 100644 --- a/alphapy/frame.py +++ b/alphapy/frame.py @@ -27,6 +27,7 @@ # from alphapy.globals import PSEP, SSEP, USEP +from alphapy.globals import TAG_ID import logging import pandas as pd @@ -258,7 +259,7 @@ def load_frames(group, directory, extension, separator, splits=False): # add this frame to the consolidated frame list if df is not None and not df.empty: # set the name - df.insert(0, 'tag', gn) + df.insert(0, TAG_ID, gn) all_frames.append(df) else: logger.debug("Empty Data Frame for: %s", gn) @@ -305,3 +306,68 @@ def dump_frames(group, directory, extension, separator): write_frame(df, directory, fname, extension, separator, index=True) else: logger.info("Data Frame for %s not found", fname) + + +# +# Function sequence_frame +# + +def sequence_frame(df, target, leaders, lag_period=1, + forecast_period=1, exclude_cols=[]): + r"""Run an analysis for a given model and group. + + Parameters + ---------- + df : pandas.DataFrame + The original dataframe. + target : str + The target variable for prediction. + leaders : list + The features that are contemporaneous with the target. + lag_period : int + The number of lagged rows for prediction. + forecast_period : int + The period for forecasting the target of the analysis. + + Returns + ------- + new_frame : pandas.DataFrame + The transformed dataframe with variable sequences. + + """ + + # Set Leading and Lagging Columns + le_cols = sorted(leaders) + le_len = len(le_cols) + df_cols = sorted(list(set(df.columns) - set(le_cols))) + for c in exclude_cols: + df_cols.remove(c) + df_len = len(df_cols) + + # Excluded Columns + new_cols, new_names = list(), list() + for c in exclude_cols: + new_cols.append(pd.DataFrame(df[c])) + new_names.append(c) + + # Lag Features + for i in range(lag_period, 0, -1): + new_cols.append(df[df_cols].shift(i)) + new_names += ['%s[%d]' % (df_cols[j], i) for j in range(df_len)] + + # Lag Leaders + for i in range(lag_period-1, -1, -1): + new_cols.append(df[le_cols].shift(i)) + if i == 0: + new_names += [le_cols[j] for j in range(le_len)] + else: + new_names += ['%s[%d]' % (le_cols[j], i) for j in range(le_len)] + + # Forecast Target(s) + new_cols.append(pd.DataFrame(df[target].shift(1-forecast_period))) + new_names.append(target) + + # Collect all columns into new frame + new_frame = pd.concat(new_cols, axis=1) + new_frame.columns = new_names + return new_frame diff --git a/alphapy/globals.py b/alphapy/globals.py index 9c26eb4..bab9462 100644 --- a/alphapy/globals.py +++ b/alphapy/globals.py @@ -58,6 +58,7 @@ # NULLTEXT = 'NULLTEXT' +TAG_ID = 'tag' WILDCARD = '*' # diff --git a/alphapy/market_flow.py b/alphapy/market_flow.py index b4e631c..aec700c 100644 --- a/alphapy/market_flow.py +++ b/alphapy/market_flow.py @@ -91,6 +91,7 @@ def get_market_config(): specs['forecast_period'] = cfg['market']['forecast_period'] specs['fractal'] = cfg['market']['fractal'] + specs['lag_period'] = cfg['market']['lag_period'] specs['leaders'] = cfg['market']['leaders'] specs['data_history'] = cfg['market']['data_history'] specs['predict_history'] = cfg['market']['predict_history'] @@ -160,11 +161,12 @@ def get_market_config(): # Log the stock parameters logger.info('MARKET PARAMETERS:') + logger.info('data_history = %d', specs['data_history']) logger.info('features = %s', specs['features']) logger.info('forecast_period = %d', specs['forecast_period']) logger.info('fractal = %s', specs['fractal']) + logger.info('lag_period = %d', specs['lag_period']) logger.info('leaders = %s', specs['leaders']) - logger.info('data_history = %d', specs['data_history']) logger.info('predict_history = %s', specs['predict_history']) logger.info('schema = %s', specs['schema']) logger.info('system = %s', specs['system']) @@ -216,6 +218,7 @@ def market_pipeline(model, market_specs): features = market_specs['features'] forecast_period = market_specs['forecast_period'] functions = market_specs['functions'] + lag_period = market_specs['lag_period'] leaders = market_specs['leaders'] predict_history = market_specs['predict_history'] target_group = market_specs['target_group'] @@ -244,10 +247,14 @@ def market_pipeline(model, market_specs): group = Group.groups[target_group] logger.info("All Members: %s", group.members) - # Get stock data + # Get stock data. If we can't get all the data, then + # predict_history resets to the actual history obtained. lookback = predict_history if predict_mode else data_history - daily = get_feed_data(group, lookback) + new_history = get_feed_data(group, lookback) + if new_history < data_history: + logger.info("Maximum Data History is %d, not %d", + new_history, data_history) # Apply the features to all of the frames @@ -269,7 +276,8 @@ def market_pipeline(model, market_specs): else: # run the analysis, including the model pipeline a = Analysis(model, group) - results = run_analysis(a, forecast_period, leaders, predict_history) + results = run_analysis(a, lag_period, forecast_period, leaders, + predict_history) # Return the completed model return model diff --git a/alphapy/model.py b/alphapy/model.py index 1649970..fd07645 100644 --- a/alphapy/model.py +++ b/alphapy/model.py @@ -38,6 +38,7 @@ from alphapy.globals import PSEP, SSEP, USEP from alphapy.globals import SamplingMethod from alphapy.globals import Scalers +from alphapy.utilities import get_datestamp from alphapy.utilities import np_store_data from copy import copy @@ -1209,10 +1210,7 @@ def save_predictions(model, tag, partition): separator = model.specs['separator'] # Get date stamp to record file creation - - d = datetime.now() - f = "%Y%m%d" - timestamp = d.strftime(f) + timestamp = get_datestamp() # Specify input and output directories diff --git a/alphapy/utilities.py b/alphapy/utilities.py index db4ae24..4323bd9 100644 --- a/alphapy/utilities.py +++ b/alphapy/utilities.py @@ -46,6 +46,25 @@ logger = logging.getLogger(__name__) +# +# Function get_datestamp +# + +def get_datestamp(): + r"""Returns today's datestamp. + + Returns + ------- + datestamp : str + The valid date string in YYYY-mm-dd format. + + """ + d = datetime.now() + f = "%Y%m%d" + datestamp = d.strftime(f) + return datestamp + + # # Function np_store_data # diff --git a/setup.py b/setup.py index b36a361..94ef85a 100644 --- a/setup.py +++ b/setup.py @@ -7,10 +7,10 @@ LONG_DESCRIPTION = "alphapy is a Python library for machine learning using scikit-learn. We have a stock market pipeline and a sports pipeline so that speculators can test predictive models, along with functions for trading systems and portfolio management." MAINTAINER = 'ScottFree LLC [Robert D. Scott II, Mark Conway]' -MAINTAINER_EMAIL = 'mark.conway@scottfreellc.com' +MAINTAINER_EMAIL = 'scottfree.analytics@scottfreellc.com' URL = "https://github.com/ScottFreeLLC/AlphaPy" -LICENSE = "Apache License, Version 2.0.1" -VERSION = "2.0.1" +LICENSE = "Apache License, Version 2.1" +VERSION = "2.1" classifiers = ['Development Status :: 4 - Beta', 'Programming Language :: Python', From e1ac84045ed7ddde71065e7c6df88316bfc44e87 Mon Sep 17 00:00:00 2001 From: Mark Conway Date: Tue, 14 Nov 2017 21:17:51 -0500 Subject: [PATCH 009/129] use finance URL use finance URL to avoid bad time stamps --- alphapy/data.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/alphapy/data.py b/alphapy/data.py index 679962f..f92da49 100644 --- a/alphapy/data.py +++ b/alphapy/data.py @@ -322,7 +322,7 @@ def get_google_data(symbol, lookback_period, fractal): toffset = 7 line_length = 6 # make the request to Google - base_url = 'https://www.google.com/finance/getprices?q={}&i={}&p={}d&f=d,o,h,l,c,v' + base_url = 'https://finance.google.com/finance/getprices?q={}&i={}&p={}d&f=d,o,h,l,c,v' url = base_url.format(symbol, interval, lookback_period) response = requests.get(url) # process the response From a8b23b187a71837894ba2cfa864e71887365ff44 Mon Sep 17 00:00:00 2001 From: Mark Conway Date: Sun, 31 Dec 2017 11:34:10 -0500 Subject: [PATCH 010/129] Add Quandl and Data Schema AlphaPy now supports local data schema and Quandl feeds using the pandas Web data reader package. --- .gitignore | 4 + alphapy/data.py | 150 +- .../Kaggle/input/gender_submission.csv | 419 ---- .../examples/Trading Model/config/market.yml | 7 +- .../Trading System/A Trading System.ipynb | 442 ++++- .../examples/Trading System/config/market.yml | 7 +- .../systems/faang_closer_positions_1d.csv | 499 +++++ .../systems/faang_closer_returns_1d.csv | 499 +++++ .../systems/faang_closer_trades_1d.csv | 1748 +++++++++++++++++ .../systems/faang_closer_transactions_1d.csv | 1748 +++++++++++++++++ alphapy/frame.py | 4 +- alphapy/globals.py | 15 +- alphapy/market_flow.py | 21 +- alphapy/system.py | 5 +- docs/tutorials/closer_market.yml | 7 +- docs/tutorials/market.rst | 4 +- docs/tutorials/rrover_market.yml | 7 +- docs/user_guide/alphapy.log | 709 +++---- setup.py | 4 +- 19 files changed, 5464 insertions(+), 835 deletions(-) delete mode 100644 alphapy/examples/Kaggle/input/gender_submission.csv create mode 100644 alphapy/examples/Trading System/systems/faang_closer_positions_1d.csv create mode 100644 alphapy/examples/Trading System/systems/faang_closer_returns_1d.csv create mode 100644 alphapy/examples/Trading System/systems/faang_closer_trades_1d.csv create mode 100644 alphapy/examples/Trading System/systems/faang_closer_transactions_1d.csv diff --git a/.gitignore b/.gitignore index 613ebe6..ea6ac42 100644 --- a/.gitignore +++ b/.gitignore @@ -24,3 +24,7 @@ .idea/vcs.xml .idea/workspace.xml +.idea/other.xml +alphapy/examples/Trading System/.ipynb_checkpoints/A Trading System-checkpoint.ipynb +*.pkl +*.png diff --git a/alphapy/data.py b/alphapy/data.py index f92da49..d15f658 100644 --- a/alphapy/data.py +++ b/alphapy/data.py @@ -31,7 +31,9 @@ from alphapy.frame import read_frame from alphapy.globals import ModelType from alphapy.globals import Partition, datasets -from alphapy.globals import PSEP, SSEP +from alphapy.globals import PD_INTRADAY_OFFSETS +from alphapy.globals import PD_WEB_DATA_FEEDS +from alphapy.globals import PSEP, SSEP, USEP from alphapy.globals import SamplingMethod from alphapy.globals import WILDCARD @@ -282,6 +284,53 @@ def sample_data(model): return model +# +# Function enhance_intraday_data +# + +def enhance_intraday_data(df): + r"""Add columns to the intraday dataframe. + + Parameters + ---------- + df : pandas.DataFrame + The intraday dataframe. + + Returns + ------- + df : pandas.DataFrame + The dataframe with bar number and end-of-day columns. + + """ + + # Convert the columns to proper data types + + index_column = 'datetime' + dt_column = df['date'] + ' ' + df['time'] + df[index_column] = pd.to_datetime(dt_column) + cols_float = ['open', 'high', 'low', 'close', 'volume'] + df[cols_float] = df[cols_float].astype(float) + + # Number the intraday bars + + date_group = df.groupby('date') + df['bar_number'] = date_group.cumcount() + + # Mark the end of the trading day + + df['end_of_day'] = False + df.loc[date_group.tail(1).index, 'end_of_day'] = True + + # Set the data frame's index + df.set_index(pd.DatetimeIndex(df[index_column]), drop=True, inplace=True) + + # Return the enhanced frame + + del df['date'] + del df['time'] + return df + + # # Function get_google_data # @@ -345,25 +394,12 @@ def get_google_data(symbol, lookback_period, fractal): dt = datetime.fromtimestamp(day_item + (interval * offset)) dt = pd.to_datetime(dt) dt_date = dt.strftime('%Y-%m-%d') - record = (dt, dt_date, open_item, high_item, low_item, close_item, volume_item) + dt_time = dt.strftime('%H:%M:%S') + record = (dt_date, dt_time, open_item, high_item, low_item, close_item, volume_item) records.append(record) # create data frame - cols = ['datetime', 'date', 'open', 'high', 'low', 'close', 'volume'] + cols = ['date', 'time', 'open', 'high', 'low', 'close', 'volume'] df = pd.DataFrame.from_records(records, columns=cols) - # convert to proper data types - cols_float = ['open', 'high', 'low', 'close'] - df[cols_float] = df[cols_float].astype(float) - df['volume'] = df['volume'].astype(int) - # number the intraday bars - date_group = df.groupby('date') - df['bar_number'] = date_group.cumcount() - # mark the end of the trading day - df['end_of_day'] = False - del df['date'] - df.loc[date_group.tail(1).index, 'end_of_day'] = True - # set the index to datetime - df.index = df['datetime'] - del df['datetime'] # return the dataframe return df @@ -373,7 +409,7 @@ def get_google_data(symbol, lookback_period, fractal): # def get_pandas_data(schema, symbol, lookback_period): - r"""Get Yahoo Finance daily data. + r"""Get Pandas Web Reader data. Parameters ---------- @@ -391,6 +427,12 @@ def get_pandas_data(schema, symbol, lookback_period): """ + # Quandl is a special case. + + if 'quandl' in schema: + schema, symbol_prefix = schema.split(USEP) + symbol = SSEP.join([symbol_prefix, symbol]).upper() + # Calculate the start and end date for Yahoo. start = datetime.now() - timedelta(lookback_period) @@ -398,25 +440,26 @@ def get_pandas_data(schema, symbol, lookback_period): # Call the Pandas Web data reader. - df = None try: df = web.DataReader(symbol, schema, start, end) - df = df.rename(columns = lambda x: x.lower().replace(' ','')) except: + df = None logger.info("Could not retrieve data for: %s", symbol) return df # -# Function get_feed_data +# Function get_market_data # -def get_feed_data(group, lookback_period): +def get_market_data(model, group, lookback_period, resample_data): r"""Get data from an external feed. Parameters ---------- + model : alphapy.Model + The model object describing the data. group : alphapy.Group The group of symbols. lookback_period : int @@ -429,27 +472,71 @@ def get_feed_data(group, lookback_period): """ + # Unpack model specifications + + directory = model.specs['directory'] + extension = model.specs['extension'] + separator = model.specs['separator'] + + # Unpack group elements + gspace = group.space schema = gspace.schema fractal = gspace.fractal + # Determine the feed source - if 'd' in fractal: - # daily data (date only) - logger.info("Getting Daily Data") - daily_data = True - else: + + if any(substring in fractal for substring in PD_INTRADAY_OFFSETS): # intraday data (date and time) - logger.info("Getting Intraday Data (Google 50-day limit)") - daily_data = False + logger.info("Getting Intraday Data [%s] from %s", fractal, schema) + intraday_data = True + index_column = 'datetime' + else: + # daily data or higher (date only) + logger.info("Getting Daily Data [%s] from %s", fractal, schema) + intraday_data = False + index_column = 'date' + # Get the data from the relevant feed + + data_dir = SSEP.join([directory, 'data']) + pandas_data = any(substring in schema for substring in PD_WEB_DATA_FEEDS) n_periods = 0 + for item in group.members: logger.info("Getting %s data for last %d days", item, lookback_period) - if daily_data: + # Locate the data source + if schema == 'data': + fname = frame_name(item.lower(), gspace) + df = read_frame(data_dir, fname, extension, separator) + if not intraday_data: + df.set_index(pd.DatetimeIndex(df[index_column]), + drop=True, inplace=True) + elif schema == 'google' and intraday_data: + df = get_google_data(item, lookback_period, fractal) + elif pandas_data: df = get_pandas_data(schema, item, lookback_period) else: - df = get_google_data(item, lookback_period, fractal) + logger.error("Unsupported Data Source: %s", schema) + # Now that we have content, standardize the data if df is not None and not df.empty: + logger.info("Rows: %d", len(df)) + # standardize column names + df = df.rename(columns = lambda x: x.lower().replace(' ','')) + # add intraday columns if necessary + if intraday_data: + df = enhance_intraday_data(df) + # order by increasing date if necessary + df = df.sort_index() + # resample data + if resample_data: + df = df.resample(fractal).agg({'open' : 'first', + 'high' : 'max', + 'low' : 'min', + 'close' : 'last', + 'volume' : 'sum'}) + logger.info("Rows after Resampling at %s: %d", + fractal, len(df)) # allocate global Frame newf = Frame(item.lower(), gspace, df) if newf is None: @@ -460,5 +547,6 @@ def get_feed_data(group, lookback_period): n_periods = df_len else: logger.info("No DataFrame for %s", item) + # The number of periods actually retrieved return n_periods diff --git a/alphapy/examples/Kaggle/input/gender_submission.csv b/alphapy/examples/Kaggle/input/gender_submission.csv deleted file mode 100644 index 7594506..0000000 --- a/alphapy/examples/Kaggle/input/gender_submission.csv +++ /dev/null @@ -1,419 +0,0 @@ -PassengerId,Survived -892,0 -893,1 -894,0 -895,0 -896,1 -897,0 -898,1 -899,0 -900,1 -901,0 -902,0 -903,0 -904,1 -905,0 -906,1 -907,1 -908,0 -909,0 -910,1 -911,1 -912,0 -913,0 -914,1 -915,0 -916,1 -917,0 -918,1 -919,0 -920,0 -921,0 -922,0 -923,0 -924,1 -925,1 -926,0 -927,0 -928,1 -929,1 -930,0 -931,0 -932,0 -933,0 -934,0 -935,1 -936,1 -937,0 -938,0 -939,0 -940,1 -941,1 -942,0 -943,0 -944,1 -945,1 -946,0 -947,0 -948,0 -949,0 -950,0 -951,1 -952,0 -953,0 -954,0 -955,1 -956,0 -957,1 -958,1 -959,0 -960,0 -961,1 -962,1 -963,0 -964,1 -965,0 -966,1 -967,0 -968,0 -969,1 -970,0 -971,1 -972,0 -973,0 -974,0 -975,0 -976,0 -977,0 -978,1 -979,1 -980,1 -981,0 -982,1 -983,0 -984,1 -985,0 -986,0 -987,0 -988,1 -989,0 -990,1 -991,0 -992,1 -993,0 -994,0 -995,0 -996,1 -997,0 -998,0 -999,0 -1000,0 -1001,0 -1002,0 -1003,1 -1004,1 -1005,1 -1006,1 -1007,0 -1008,0 -1009,1 -1010,0 -1011,1 -1012,1 -1013,0 -1014,1 -1015,0 -1016,0 -1017,1 -1018,0 -1019,1 -1020,0 -1021,0 -1022,0 -1023,0 -1024,1 -1025,0 -1026,0 -1027,0 -1028,0 -1029,0 -1030,1 -1031,0 -1032,1 -1033,1 -1034,0 -1035,0 -1036,0 -1037,0 -1038,0 -1039,0 -1040,0 -1041,0 -1042,1 -1043,0 -1044,0 -1045,1 -1046,0 -1047,0 -1048,1 -1049,1 -1050,0 -1051,1 -1052,1 -1053,0 -1054,1 -1055,0 -1056,0 -1057,1 -1058,0 -1059,0 -1060,1 -1061,1 -1062,0 -1063,0 -1064,0 -1065,0 -1066,0 -1067,1 -1068,1 -1069,0 -1070,1 -1071,1 -1072,0 -1073,0 -1074,1 -1075,0 -1076,1 -1077,0 -1078,1 -1079,0 -1080,1 -1081,0 -1082,0 -1083,0 -1084,0 -1085,0 -1086,0 -1087,0 -1088,0 -1089,1 -1090,0 -1091,1 -1092,1 -1093,0 -1094,0 -1095,1 -1096,0 -1097,0 -1098,1 -1099,0 -1100,1 -1101,0 -1102,0 -1103,0 -1104,0 -1105,1 -1106,1 -1107,0 -1108,1 -1109,0 -1110,1 -1111,0 -1112,1 -1113,0 -1114,1 -1115,0 -1116,1 -1117,1 -1118,0 -1119,1 -1120,0 -1121,0 -1122,0 -1123,1 -1124,0 -1125,0 -1126,0 -1127,0 -1128,0 -1129,0 -1130,1 -1131,1 -1132,1 -1133,1 -1134,0 -1135,0 -1136,0 -1137,0 -1138,1 -1139,0 -1140,1 -1141,1 -1142,1 -1143,0 -1144,0 -1145,0 -1146,0 -1147,0 -1148,0 -1149,0 -1150,1 -1151,0 -1152,0 -1153,0 -1154,1 -1155,1 -1156,0 -1157,0 -1158,0 -1159,0 -1160,1 -1161,0 -1162,0 -1163,0 -1164,1 -1165,1 -1166,0 -1167,1 -1168,0 -1169,0 -1170,0 -1171,0 -1172,1 -1173,0 -1174,1 -1175,1 -1176,1 -1177,0 -1178,0 -1179,0 -1180,0 -1181,0 -1182,0 -1183,1 -1184,0 -1185,0 -1186,0 -1187,0 -1188,1 -1189,0 -1190,0 -1191,0 -1192,0 -1193,0 -1194,0 -1195,0 -1196,1 -1197,1 -1198,0 -1199,0 -1200,0 -1201,1 -1202,0 -1203,0 -1204,0 -1205,1 -1206,1 -1207,1 -1208,0 -1209,0 -1210,0 -1211,0 -1212,0 -1213,0 -1214,0 -1215,0 -1216,1 -1217,0 -1218,1 -1219,0 -1220,0 -1221,0 -1222,1 -1223,0 -1224,0 -1225,1 -1226,0 -1227,0 -1228,0 -1229,0 -1230,0 -1231,0 -1232,0 -1233,0 -1234,0 -1235,1 -1236,0 -1237,1 -1238,0 -1239,1 -1240,0 -1241,1 -1242,1 -1243,0 -1244,0 -1245,0 -1246,1 -1247,0 -1248,1 -1249,0 -1250,0 -1251,1 -1252,0 -1253,1 -1254,1 -1255,0 -1256,1 -1257,1 -1258,0 -1259,1 -1260,1 -1261,0 -1262,0 -1263,1 -1264,0 -1265,0 -1266,1 -1267,1 -1268,1 -1269,0 -1270,0 -1271,0 -1272,0 -1273,0 -1274,1 -1275,1 -1276,0 -1277,1 -1278,0 -1279,0 -1280,0 -1281,0 -1282,0 -1283,1 -1284,0 -1285,0 -1286,0 -1287,1 -1288,0 -1289,1 -1290,0 -1291,0 -1292,1 -1293,0 -1294,1 -1295,0 -1296,0 -1297,0 -1298,0 -1299,0 -1300,1 -1301,1 -1302,1 -1303,1 -1304,1 -1305,0 -1306,1 -1307,0 -1308,0 -1309,0 diff --git a/alphapy/examples/Trading Model/config/market.yml b/alphapy/examples/Trading Model/config/market.yml index c7614a1..3ed085e 100644 --- a/alphapy/examples/Trading Model/config/market.yml +++ b/alphapy/examples/Trading Model/config/market.yml @@ -1,10 +1,13 @@ market: - data_history : 2000 + data_history : 500 forecast_period : 1 fractal : 1d + lag_period : 1 leaders : ['gap', 'gapbadown', 'gapbaup', 'gapdown', 'gapup'] predict_history : 100 - schema : google + resample_data : False + schema : yahoo + subject : stock target_group : test groups: diff --git a/alphapy/examples/Trading System/A Trading System.ipynb b/alphapy/examples/Trading System/A Trading System.ipynb index 9d51134..67ea416 100644 --- a/alphapy/examples/Trading System/A Trading System.ipynb +++ b/alphapy/examples/Trading System/A Trading System.ipynb @@ -2,52 +2,466 @@ "cells": [ { "cell_type": "code", - "execution_count": null, - "metadata": { - "collapsed": true - }, + "execution_count": 1, + "metadata": {}, "outputs": [], "source": [ + "%matplotlib inline\n", "import pandas as pd\n", "import pyfolio as pf" ] }, { "cell_type": "code", - "execution_count": null, + "execution_count": 2, "metadata": {}, - "outputs": [], + "outputs": [ + { + "data": { + "text/plain": [ + "'/Users/markconway/Projects/AlphaPy/alphapy/examples/Trading System'" + ] + }, + "execution_count": 2, + "metadata": {}, + "output_type": "execute_result" + } + ], "source": [ "pwd" ] }, { "cell_type": "code", - "execution_count": null, + "execution_count": 3, "metadata": {}, - "outputs": [], + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "/Users/markconway/Projects/AlphaPy/alphapy/examples/Trading System/systems\n" + ] + } + ], "source": [ "cd systems" ] }, { "cell_type": "code", - "execution_count": null, + "execution_count": 4, "metadata": {}, - "outputs": [], + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "faang_closer_positions_1d.csv faang_closer_trades_1d.csv\r\n", + "faang_closer_returns_1d.csv faang_closer_transactions_1d.csv\r\n" + ] + } + ], "source": [ "ls" ] }, { "cell_type": "code", - "execution_count": null, + "execution_count": 5, "metadata": {}, "outputs": [], "source": [ "df = pd.read_csv('faang_closer_returns_1d.csv', index_col='date', squeeze=True)\n", - "df.index = pd.to_datetime(df.index, utc=True)\n", - "pf.create_returns_tear_sheet(df)" + "df.index = pd.to_datetime(df.index, utc=True)" + ] + }, + { + "cell_type": "code", + "execution_count": 6, + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "" + ] + }, + "execution_count": 6, + "metadata": {}, + "output_type": "execute_result" + }, + { + "data": { + "image/png": "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\n", + "text/plain": [ + "" + ] + }, + "metadata": {}, + "output_type": "display_data" + } + ], + "source": [ + "pf.plot_monthly_returns_heatmap(df)" + ] + }, + { + "cell_type": "code", + "execution_count": 7, + "metadata": {}, + "outputs": [ + { + "name": "stderr", + "output_type": "stream", + "text": [ + "/Users/markconway/anaconda/lib/python3.6/site-packages/numpy/core/fromnumeric.py:57: FutureWarning: 'argmin' is deprecated. Use 'idxmin' instead. The behavior of 'argmin' will be corrected to return the positional minimum in the future. Use 'series.values.argmin' to get the position of the minimum now.\n", + " return getattr(obj, method)(*args, **kwds)\n" + ] + }, + { + "data": { + "text/plain": [ + "" + ] + }, + "execution_count": 7, + "metadata": {}, + "output_type": "execute_result" + }, + { + "data": { + "image/png": "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\n", + "text/plain": [ + "" + ] + }, + "metadata": {}, + "output_type": "display_data" + } + ], + "source": [ + "pf.plot_drawdown_periods(df, 5)" + ] + }, + { + "cell_type": "code", + "execution_count": 8, + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "" + ] + }, + "execution_count": 8, + "metadata": {}, + "output_type": "execute_result" + }, + { + "data": { + "image/png": "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\n", + "text/plain": [ + "" + ] + }, + "metadata": {}, + "output_type": "display_data" + } + ], + "source": [ + "pf.plot_drawdown_underwater(df)" + ] + }, + { + "cell_type": "code", + "execution_count": 9, + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "" + ] + }, + "execution_count": 9, + "metadata": {}, + "output_type": "execute_result" + }, + { + "data": { + "image/png": "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\n", + "text/plain": [ + "" + ] + }, + "metadata": {}, + "output_type": "display_data" + } + ], + "source": [ + "pf.plot_returns(df)" + ] + }, + { + "cell_type": "code", + "execution_count": 10, + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "" + ] + }, + "execution_count": 10, + "metadata": {}, + "output_type": "execute_result" + }, + { + "data": { + "image/png": "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\n", + "text/plain": [ + "" + ] + }, + "metadata": {}, + "output_type": "display_data" + } + ], + "source": [ + "pf.plot_rolling_returns(df)" + ] + }, + { + "cell_type": "code", + "execution_count": 11, + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "" + ] + }, + "execution_count": 11, + "metadata": {}, + "output_type": "execute_result" + }, + { + "data": { + "image/png": "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\n", + "text/plain": [ + "" + ] + }, + "metadata": {}, + "output_type": "display_data" + } + ], + "source": [ + "pf.plot_annual_returns(df)" + ] + }, + { + "cell_type": "code", + "execution_count": 12, + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "" + ] + }, + "execution_count": 12, + "metadata": {}, + "output_type": "execute_result" + }, + { + "data": { + "image/png": "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\n", + "text/plain": [ + "" + ] + }, + "metadata": {}, + "output_type": "display_data" + } + ], + "source": [ + "pf.plot_monthly_returns_dist(df)" + ] + }, + { + "cell_type": "code", + "execution_count": 13, + "metadata": {}, + "outputs": [ + { + "name": "stderr", + "output_type": "stream", + "text": [ + "/Users/markconway/anaconda/lib/python3.6/site-packages/numpy/core/fromnumeric.py:57: FutureWarning: 'argmin' is deprecated. Use 'idxmin' instead. The behavior of 'argmin' will be corrected to return the positional minimum in the future. Use 'series.values.argmin' to get the position of the minimum now.\n", + " return getattr(obj, method)(*args, **kwds)\n" + ] + }, + { + "data": { + "text/html": [ + "
\n", + "\n", + "\n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + "
Worst drawdown periodsNet drawdown in %Peak dateValley dateRecovery dateDuration
011.612016-10-242016-11-142017-01-1964
17.422017-06-082017-07-032017-07-1829
25.042017-11-282017-12-04NaTNaN
34.642017-07-242017-09-252017-10-0554
43.202016-09-072016-09-092016-09-157
\n", + "
" + ], + "text/plain": [ + "Worst drawdown periods Net drawdown in % Peak date Valley date Recovery date \\\n", + "0 11.61 2016-10-24 2016-11-14 2017-01-19 \n", + "1 7.42 2017-06-08 2017-07-03 2017-07-18 \n", + "2 5.04 2017-11-28 2017-12-04 NaT \n", + "3 4.64 2017-07-24 2017-09-25 2017-10-05 \n", + "4 3.20 2016-09-07 2016-09-09 2016-09-15 \n", + "\n", + "Worst drawdown periods Duration \n", + "0 64 \n", + "1 29 \n", + "2 NaN \n", + "3 54 \n", + "4 7 " + ] + }, + "metadata": {}, + "output_type": "display_data" + } + ], + "source": [ + "pf.show_worst_drawdown_periods(df)" + ] + }, + { + "cell_type": "code", + "execution_count": 14, + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "" + ] + }, + "execution_count": 14, + "metadata": {}, + "output_type": "execute_result" + }, + { + "data": { + "image/png": "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\n", + "text/plain": [ + "" + ] + }, + "metadata": {}, + "output_type": "display_data" + } + ], + "source": [ + "pf.plot_return_quantiles(df)" + ] + }, + { + "cell_type": "code", + "execution_count": 15, + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "" + ] + }, + "execution_count": 15, + "metadata": {}, + "output_type": "execute_result" + }, + { + "data": { + "image/png": "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\n", + "text/plain": [ + "" + ] + }, + "metadata": {}, + "output_type": "display_data" + } + ], + "source": [ + "pf.plot_rolling_sharpe(df)" ] } ], @@ -67,7 +481,7 @@ "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", - "version": "3.6.0" + "version": "3.6.4" } }, "nbformat": 4, diff --git a/alphapy/examples/Trading System/config/market.yml b/alphapy/examples/Trading System/config/market.yml index 6991b1f..d01f3bd 100644 --- a/alphapy/examples/Trading System/config/market.yml +++ b/alphapy/examples/Trading System/config/market.yml @@ -1,10 +1,13 @@ market: - data_history : 1000 + data_history : 500 forecast_period : 1 fractal : 1d + lag_period : 1 leaders : [] predict_history : 50 - schema : google + resample_data : False + schema : quandl_wiki + subject : stock target_group : faang system: diff --git a/alphapy/examples/Trading System/systems/faang_closer_positions_1d.csv b/alphapy/examples/Trading System/systems/faang_closer_positions_1d.csv new file mode 100644 index 0000000..926344a --- /dev/null +++ b/alphapy/examples/Trading System/systems/faang_closer_positions_1d.csv @@ -0,0 +1,499 @@ +date,nflx,amzn,fb,aapl,googl,cash +2016-08-19,-19940.96,-19690.059999999998,-19893.16,19903.52,-19991.25,581.0500000000065 +2016-08-20,-19940.96,-19690.059999999998,-19893.16,19903.52,-19991.25,581.0500000000065 +2016-08-21,-19940.96,-19690.059999999998,-19893.16,19903.52,-19991.25,581.0500000000065 +2016-08-22,-19940.96,19746.48,19988.15,-19965.84,-19991.25,364.0300000000061 +2016-08-23,19955.52,19746.48,19988.15,19919.55,-19923.75,472.88000000000466 +2016-08-24,-19987.800000000003,-19688.5,-20003.760000000002,-19985.55,-19914.75,-57.0199999999968 +2016-08-25,19853.28,19739.72,19822.4,-19985.55,-19840.0,774.6800000000003 +2016-08-26,19853.28,19739.72,19822.4,-19900.449999999997,19830.5,774.6800000000003 +2016-08-27,19906.32,19994.0,19993.6,-19783.899999999998,19830.5,774.6800000000003 +2016-08-28,19906.32,19994.0,19993.6,-19783.899999999998,19830.5,774.6800000000003 +2016-08-29,-20043.8,19994.0,19993.6,-19783.899999999998,19830.5,580.0800000000017 +2016-08-30,20074.7,-19957.08,-20008.56,-19761.699999999997,-19798.0,705.9200000000019 +2016-08-31,20074.7,19998.16,19926.96,19946.8,-19798.0,513.7400000000052 +2016-09-01,-19962.899999999998,19998.16,19926.96,19946.8,19785.0,611.1200000000063 +2016-09-02,-19962.899999999998,20036.12,19934.86,20065.24,19785.0,611.1200000000063 +2016-09-03,-19962.899999999998,20083.440000000002,19988.58,20253.24,19921.75,611.1200000000063 +2016-09-04,-19962.899999999998,20083.440000000002,19988.58,20253.24,19921.75,611.1200000000063 +2016-09-05,-19962.899999999998,20083.440000000002,19988.58,20253.24,19921.75,611.1200000000063 +2016-09-06,20118.09,20083.440000000002,19988.58,-20139.9,19921.75,1119.1800000000076 +2016-09-07,-20424.9,-20396.48,20497.34,20480.04,-20199.75,406.7100000000064 +2016-09-08,20430.3,-20396.48,-20452.390000000003,-20470.88,-20199.75,109.04000000000087 +2016-09-09,-20361.5,-20385.559999999998,-20452.390000000003,-20470.88,-20071.0,-469.9599999999991 +2016-09-10,-20361.5,-19763.64,-19954.7,-20007.219999999998,-19712.0,-469.9599999999991 +2016-09-11,-20361.5,-19763.64,-19954.7,-20007.219999999998,-19712.0,-469.9599999999991 +2016-09-12,19810.0,19287.25,19818.26,19822.72,19171.68,3208.6100000000006 +2016-09-13,-20178.9,-19786.26,-20099.18,19822.72,-19718.0,189.13999999999942 +2016-09-14,19984.06,19788.34,19932.12,20294.600000000002,19761.5,832.7200000000012 +2016-09-15,19984.06,19788.34,19932.12,21012.76,19761.5,832.7200000000012 +2016-09-16,20052.04,20011.940000000002,20022.6,-20455.760000000002,-19949.25,1981.9199999999983 +2016-09-17,20492.88,20241.52,20134.92,-20455.760000000002,-19949.25,1981.9199999999983 +2016-09-18,20492.88,20241.52,20134.92,-20455.760000000002,-19949.25,1981.9199999999983 +2016-09-19,-20592.600000000002,-20152.600000000002,-20584.0,-20455.760000000002,-19949.25,1075.079999999998 +2016-09-20,20436.0,20285.72,-20584.0,-20217.239999999998,19994.5,1271.579999999998 +2016-09-21,-20494.079999999998,20285.72,20530.52,-20215.46,19994.5,772.4200000000019 +2016-09-22,20507.62,20533.24,20530.52,20516.98,20125.75,849.4600000000028 +2016-09-23,20507.62,20922.2,-20729.52,-20738.64,-20374.0,-225.92999999999665 +2016-09-24,20531.16,20949.5,-20729.52,-20738.64,-20374.0,-225.92999999999665 +2016-09-25,20531.16,20949.5,-20729.52,-20738.64,-20374.0,-225.92999999999665 +2016-09-26,-20614.08,-19979.0,-20729.52,20544.16,-20374.0,420.75 +2016-09-27,20384.699999999997,20402.75,20333.02,20544.16,20268.25,1712.0699999999997 +2016-09-28,20384.699999999997,20402.75,20333.02,20582.38,-20251.5,1712.0699999999997 +2016-09-29,-20784.05,20718.0,-20750.58,-20753.300000000003,-20251.5,379.8199999999997 +2016-09-30,20596.95,20726.25,20651.47,20688.149999999998,20101.5,1325.4900000000016 +2016-10-01,20596.95,20932.75,20651.47,20688.149999999998,20101.5,1325.4900000000016 +2016-10-02,20596.95,20932.75,20651.47,20688.149999999998,20101.5,1325.4900000000016 +2016-10-03,20596.95,-20081.760000000002,20651.47,-20816.2,-20809.88,1136.8099999999977 +2016-10-04,-20979.7,-20081.760000000002,-20894.97,20905.0,20872.54,1289.7899999999972 +2016-10-05,20937.16,20264.64,20940.61,20905.0,-20831.98,2140.029999999999 +2016-10-06,-21119.07,-21041.5,20940.61,20914.25,20880.08,878.0899999999965 +2016-10-07,-21119.07,-21041.5,20984.620000000003,21069.65,-20818.46,878.0899999999965 +2016-10-08,-21068.82,-20985.75,21025.370000000003,21101.100000000002,-20818.46,878.0899999999965 +2016-10-09,-21068.82,-20985.75,21025.370000000003,21101.100000000002,-20818.46,878.0899999999965 +2016-10-10,-21068.82,21042.75,21025.370000000003,21101.100000000002,21168.42,878.0899999999965 +2016-10-11,-20769.329999999998,-20775.0,-21265.2,21469.25,-21048.82,620.3299999999945 +2016-10-12,-20218.59,20852.25,21035.15,21515.5,20294.25,1690.199999999997 +2016-10-13,21048.3,-20732.0,-21090.3,-21056.4,-20906.08,313.30999999999403 +2016-10-14,21048.3,-20732.0,20972.32,20938.14,20919.600000000002,676.4499999999935 +2016-10-15,21308.7,-20574.0,20972.32,20938.14,20919.600000000002,676.4499999999935 +2016-10-16,21308.7,-20574.0,20972.32,20938.14,20919.600000000002,676.4499999999935 +2016-10-17,-21057.8,-20574.0,-21044.100000000002,-21041.45,20919.600000000002,331.5599999999904 +2016-10-18,20907.04,20441.25,20828.34,-21041.45,20977.84,4874.919999999991 +2016-10-19,20907.04,20441.25,20828.34,-21027.13,21358.74,4874.919999999991 +2016-10-20,21449.120000000003,-21878.640000000003,-21970.0,-20964.48,-21362.38,2344.279999999988 +2016-10-21,21709.6,21293.74,21923.62,-20953.74,21425.559999999998,3559.479999999985 +2016-10-22,22440.0,21293.74,21923.62,-20871.399999999998,21425.559999999998,3559.479999999985 +2016-10-23,22440.0,21293.74,21923.62,-20871.399999999998,21425.559999999998,3559.479999999985 +2016-10-24,-22282.75,21293.74,21923.62,22235.850000000002,21425.559999999998,2510.309999999983 +2016-10-25,-22282.75,-21714.68,-22489.3,22235.850000000002,-22370.85,1152.599999999984 +2016-10-26,22346.72,-21714.68,-22489.3,-22424.46,-22370.85,447.67999999998574 +2016-10-27,-22132.25,-21387.34,-22276.8,-22424.46,-22196.7,574.1499999999869 +2016-10-28,22023.18,-21277.36,22056.719999999998,-22209.12,21308.559999999998,1782.8599999999897 +2016-10-29,22023.18,-20184.32,22056.719999999998,-22061.68,21308.559999999998,1782.8599999999897 +2016-10-30,22023.18,-20184.32,22056.719999999998,-22061.68,21308.559999999998,1782.8599999999897 +2016-10-31,-21852.25,21325.140000000003,-21875.33,-22061.68,-21867.3,189.25999999998385 +2016-11-01,-21852.25,-21206.07,-21875.33,-22026.760000000002,-21867.3,189.25999999998385 +2016-11-02,-21577.5,-21206.07,-21626.5,21536.87,-21747.96,300.849999999984 +2016-11-03,-21409.5,20709.809999999998,-21237.39,-21197.19,-21287.34,300.849999999984 +2016-11-04,-21374.5,-20386.35,20889.75,-21197.19,-21119.13,-423.650000000016 +2016-11-05,-21355.25,-20386.35,20889.75,-21006.12,-21089.7,-423.650000000016 +2016-11-06,-21355.25,-20386.35,20889.75,-21006.12,-21089.7,-423.650000000016 +2016-11-07,20804.86,20408.18,20889.75,20757.079999999998,20852.78,2711.999999999982 +2016-11-08,-21262.14,20408.18,21131.95,20757.079999999998,20852.78,2214.6399999999812 +2016-11-09,-21262.14,-20840.76,-21433.32,-21399.84,-20945.34,765.1799999999821 +2016-11-10,-20894.489999999998,-20840.76,-21433.32,-21399.84,-20945.34,765.1799999999821 +2016-11-11,-19736.82,-20044.26,-21019.2,20493.27,-20287.54,1198.8999999999833 +2016-11-12,-19627.38,-19953.27,-20709.48,20493.27,-20065.5,1198.8999999999833 +2016-11-13,-19627.38,-19953.27,-20709.48,20493.27,-20065.5,1198.8999999999833 +2016-11-14,-19627.38,-19953.27,-20709.48,-20402.03,-20065.5,776.0599999999831 +2016-11-15,19878.25,19324.24,19806.8,19815.35,19379.0,3282.9799999999814 +2016-11-16,19878.25,19324.24,-20243.16,19815.35,19379.0,2701.2799999999806 +2016-11-17,-20360.31,19408.74,20377.670000000002,-20450.7,19499.5,2479.0599999999795 +2016-11-18,20507.379999999997,19666.399999999998,-20595.52,20581.22,-20175.22,1126.7599999999766 +2016-11-19,20507.379999999997,19764.16,-20595.52,20581.22,-20175.22,1126.7599999999766 +2016-11-20,20507.379999999997,19764.16,-20595.52,20581.22,-20175.22,1126.7599999999766 +2016-11-21,20507.379999999997,19764.16,20457.36,20581.22,20404.8,2100.9199999999764 +2016-11-22,20996.879999999997,20280.0,-21014.31,20893.510000000002,20404.8,1493.5699999999742 +2016-11-23,-20948.82,-20283.12,-21014.31,-21022.47,-21033.0,492.1099999999751 +2016-11-24,-20948.82,-20283.12,-20905.32,-21022.47,-21033.0,492.1099999999751 +2016-11-25,-20948.82,20289.62,-20905.32,20904.73,20285.98,1495.9199999999764 +2016-11-26,-20898.98,20289.62,-20825.739999999998,20904.73,20285.98,1495.9199999999764 +2016-11-27,-20898.98,20289.62,-20825.739999999998,20904.73,20285.98,1495.9199999999764 +2016-11-28,-20898.98,-20702.79,20830.93,-20863.59,20285.98,729.149999999976 +2016-11-29,20799.27,-20702.79,20830.93,-20863.59,20430.54,846.659999999978 +2016-11-30,-20826.0,-20588.04,-20841.920000000002,-20843.02,-20172.88,374.399999999976 +2016-12-01,20513.5,-20265.390000000003,-20841.920000000002,-20667.239999999998,-20172.88,726.0599999999758 +2016-12-02,20513.5,-20078.55,20310.4,20331.5,19875.96,945.8599999999751 +2016-12-03,21141.75,-19989.18,20310.4,20331.5,19875.96,945.8599999999751 +2016-12-04,21141.75,-19989.18,20310.4,20331.5,19875.96,945.8599999999751 +2016-12-05,-20495.52,20502.72,20310.4,-20512.68,19875.96,976.0099999999729 +2016-12-06,20554.05,20502.72,-20646.56,20670.600000000002,-20180.68,1847.999999999971 +2016-12-07,20554.05,20647.440000000002,20877.15,20670.600000000002,20578.22,1730.0499999999702 +2016-12-08,-21074.04,-20717.91,20877.15,20873.64,20578.22,990.6099999999678 +2016-12-09,-21074.04,20753.82,21047.07,21078.56,20674.42,990.6099999999678 +2016-12-10,-21012.48,20753.82,21183.36,21422.600000000002,21045.7,990.6099999999678 +2016-12-11,-21012.48,20753.82,21183.36,21422.600000000002,21045.7,990.6099999999678 +2016-12-12,-21012.48,-20523.24,-21198.6,-21187.1,-21005.399999999998,750.5999999999694 +2016-12-13,21042.6,20907.18,21054.25,21079.77,20383.5,2752.0299999999697 +2016-12-14,-21355.12,-20758.140000000003,-21397.379999999997,21079.77,20383.5,2021.0799999999726 +2016-12-15,21375.0,-20758.140000000003,21340.89,21079.77,-21206.899999999998,1575.9999999999745 +2016-12-16,-21365.84,-20547.0,-21336.86,21195.059999999998,-21206.899999999998,1331.9099999999744 +2016-12-17,-21365.84,-20459.79,-21336.86,21222.51,-21055.84,1331.9099999999744 +2016-12-18,-21365.84,-20459.79,-21336.86,21222.51,-21055.84,1331.9099999999744 +2016-12-19,21326.5,20682.0,-21336.86,21222.51,21125.0,1582.8099999999758 +2016-12-20,-21395.52,20682.0,-21224.719999999998,21345.12,21125.0,1457.6899999999769 +2016-12-21,21378.5,-20806.2,-21198.02,21401.850000000002,-21117.2,1710.6899999999769 +2016-12-22,-21474.18,-20806.2,-21189.120000000003,-21513.65,-21117.2,1226.9499999999753 +2016-12-23,21350.3,-20691.18,-20897.2,21323.16,-21051.68,1585.5799999999763 +2016-12-24,21350.3,-20535.93,-20874.059999999998,21323.16,-21002.8,1585.5799999999763 +2016-12-25,21350.3,-20535.93,-20874.059999999998,21323.16,-21002.8,1585.5799999999763 +2016-12-26,21350.3,-20535.93,-20874.059999999998,21323.16,-21002.8,1585.5799999999763 +2016-12-27,21350.3,20827.8,21241.8,21323.16,21058.18,1349.5599999999795 +2016-12-28,-21527.19,20827.8,-21513.28,-21483.84,-20918.82,639.2299999999814 +2016-12-29,-21527.19,-20659.05,-21513.28,-21483.84,-20918.82,639.2299999999814 +2016-12-30,-21431.43,-20659.05,-21408.399999999998,-21478.32,-20874.88,639.2299999999814 +2016-12-31,-21169.8,-20246.49,-21169.2,-21310.879999999997,-20603.7,639.2299999999814 +2017-01-01,-21169.8,-20246.49,-21169.2,-21310.879999999997,-20603.7,639.2299999999814 +2017-01-02,-21169.8,-20246.49,-21169.2,-21310.879999999997,-20603.7,639.2299999999814 +2017-01-03,20908.36,20349.09,20917.94,21023.15,21008.26,2464.4099999999853 +2017-01-04,20908.36,20349.09,20917.94,-21231.66,-21002.02,2232.3699999999844 +2017-01-05,21223.239999999998,20443.859999999997,21245.51,21456.24,21138.52,2115.759999999984 +2017-01-06,-21757.62,21072.15,21599.93,21456.24,21138.52,1853.6199999999844 +2017-01-07,-21757.62,21491.73,22090.39,21695.44,21455.46,1853.6199999999844 +2017-01-08,-21757.62,21491.73,22090.39,21695.44,21455.46,1853.6199999999844 +2017-01-09,-21757.62,21491.73,22090.39,21695.44,21455.46,1853.6199999999844 +2017-01-10,-21737.699999999997,-21489.3,-22134.3,21894.16,-21476.26,1977.969999999983 +2017-01-11,22054.5,21573.54,22065.75,21916.24,21576.36,1964.7399999999834 +2017-01-12,-22218.960000000003,21573.54,22065.75,-22180.5,-21567.78,1338.6999999999825 +2017-01-13,22194.199999999997,21968.28,22158.5,-22180.5,21604.440000000002,2140.8999999999833 +2017-01-14,22194.199999999997,22062.78,22459.5,-22141.440000000002,21604.440000000002,2140.8999999999833 +2017-01-15,22194.199999999997,22062.78,22459.5,-22141.440000000002,21604.440000000002,2140.8999999999833 +2017-01-16,22194.199999999997,22062.78,22459.5,-22141.440000000002,21604.440000000002,2140.8999999999833 +2017-01-17,-22458.409999999996,-21862.440000000002,-22505.120000000003,22440.0,-22341.420000000002,666.8999999999796 +2017-01-18,22387.68,-21862.440000000002,22386.0,-22438.129999999997,22383.54,928.0799999999799 +2017-01-19,22387.68,21844.079999999998,-22448.8,-22438.129999999997,-22257.99,800.5299999999806 +2017-01-20,23252.88,-21824.91,-22448.8,22560.0,22360.59,680.5299999999806 +2017-01-21,23284.8,-21824.91,-22359.04,22560.0,22360.59,680.5299999999806 +2017-01-22,23284.8,-21824.91,-22359.04,22560.0,22360.59,680.5299999999806 +2017-01-23,-22531.96,22082.76,22562.75,22560.0,22360.59,1359.0199999999786 +2017-01-24,22697.820000000003,22082.76,22562.75,-22674.329999999998,22799.609999999997,1519.2699999999786 +2017-01-25,-22881.280000000002,22205.88,22639.75,22913.44,22937.309999999998,1362.1099999999788 +2017-01-26,-22881.280000000002,22586.04,23009.0,22913.44,-23138.46,1362.1099999999788 +2017-01-27,23219.35,-22565.79,-23131.5,22924.72,-23138.46,1504.5599999999795 +2017-01-28,23219.35,-22565.79,-23131.5,22926.600000000002,-22815.809999999998,1504.5599999999795 +2017-01-29,23219.35,-22565.79,-23131.5,22926.600000000002,-22815.809999999998,1504.5599999999795 +2017-01-30,-23160.079999999998,-22565.79,-23131.5,-23231.329999999998,-22815.809999999998,998.4499999999825 +2017-01-31,-23160.079999999998,-22420.26,-22921.5,-23231.329999999998,-22243.41,998.4499999999825 +2017-02-01,22806.36,22473.45,22782.329999999998,22788.75,-22145.13,3615.4299999999857 +2017-02-02,-23246.399999999998,22473.45,-23289.52,-23263.93,22911.28,671.1699999999873 +2017-02-03,23141.25,-22685.600000000002,23183.46,23105.320000000003,22911.28,530.609999999986 +2017-02-04,23141.25,-22685.600000000002,23183.46,23105.320000000003,22963.64,530.609999999986 +2017-02-05,23141.25,-22685.600000000002,23183.46,23105.320000000003,22963.64,530.609999999986 +2017-02-06,23141.25,-22685.600000000002,23183.46,23105.320000000003,22963.64,530.609999999986 +2017-02-07,23260.05,22750.0,-23203.84,23321.91,23005.36,662.4499999999862 +2017-02-08,23760.0,22750.0,23350.8,23543.87,23218.440000000002,930.849999999984 +2017-02-09,-23494.819999999996,22951.88,-23474.499999999996,23635.16,23236.64,1084.989999999987 +2017-02-10,23460.84,22998.08,23483.25,-23517.36,23241.68,1361.9299999999857 +2017-02-11,23460.84,23168.88,23483.25,-23517.36,23375.8,1361.9299999999857 +2017-02-12,23460.84,23168.88,23483.25,-23517.36,23375.8,1361.9299999999857 +2017-02-13,-23627.999999999996,23168.88,-23592.800000000003,23592.329999999998,23375.8,931.5699999999888 +2017-02-14,-23627.999999999996,-23418.92,-23592.800000000003,23592.329999999998,23490.88,931.5699999999888 +2017-02-15,23616.820000000003,23595.600000000002,-23557.6,23898.54,-23444.960000000003,789.2999999999884 +2017-02-16,-23715.67,23595.600000000002,23689.68,-23685.375,23580.76,784.1399999999885 +2017-02-17,23750.74,23635.92,-23768.34,23751.0,23580.76,650.6099999999897 +2017-02-18,23750.74,23661.960000000003,-23768.34,23751.0,23703.399999999998,650.6099999999897 +2017-02-19,23750.74,23661.960000000003,-23768.34,23751.0,23703.399999999998,650.6099999999897 +2017-02-20,23750.74,23661.960000000003,-23768.34,23751.0,23703.399999999998,650.6099999999897 +2017-02-21,23750.74,23661.960000000003,23802.16,23751.0,23703.399999999998,650.6099999999897 +2017-02-22,23814.2,-23957.08,23802.16,23922.499999999996,23779.559999999998,650.6099999999897 +2017-02-23,-24129.82,-23957.08,-24094.08,-24029.28,-23828.0,228.51999999998952 +2017-02-24,23922.75,-23861.32,23972.88,23915.5,-23828.0,787.1199999999881 +2017-02-25,23922.75,-23666.72,23972.88,23915.5,-23738.68,787.1199999999881 +2017-02-26,23922.75,-23666.72,23972.88,23915.5,-23738.68,787.1199999999881 +2017-02-27,23922.75,23761.92,23972.88,23915.5,23790.76,787.1199999999881 +2017-02-28,-24019.969999999998,-23661.12,-23990.579999999998,23962.75,-23658.039999999997,502.85999999998967 +2017-03-01,23822.55,23886.24,23911.079999999998,23973.25,23132.25,2057.169999999991 +2017-03-02,-24138.69,-23769.48,-24206.519999999997,-24179.04,-23795.8,98.81999999999243 +2017-03-03,-24138.69,23796.64,24004.749999999996,23902.38,-23795.8,792.4999999999927 +2017-03-04,-24071.219999999998,23796.64,24004.749999999996,23902.38,-23774.24,792.4999999999927 +2017-03-05,-24071.219999999998,23796.64,24004.749999999996,23902.38,-23774.24,792.4999999999927 +2017-03-06,23987.86,-23705.08,24004.749999999996,-23966.48,-23774.24,1220.919999999991 +2017-03-07,-24043.100000000002,-23705.08,-24027.500000000004,23997.440000000002,23832.2,1079.4899999999907 +2017-03-08,-24043.100000000002,23814.0,24101.0,-24047.0,23832.2,940.4899999999907 +2017-03-09,24030.63,23814.0,24101.0,-24047.0,23901.92,799.9599999999882 +2017-03-10,24030.63,-23868.88,24192.0,24071.219999999998,24019.52,799.9599999999882 +2017-03-11,24092.19,-23868.88,24288.25,24071.219999999998,24119.34,799.9599999999882 +2017-03-12,24092.19,-23868.88,24288.25,24071.219999999998,24119.34,799.9599999999882 +2017-03-13,24092.19,23928.52,24288.25,24071.219999999998,24119.34,799.9599999999882 +2017-03-14,-24342.3,-23870.84,-24381.0,-24323.25,24208.24,665.1699999999873 +2017-03-15,24256.75,23883.16,24311.28,24299.58,24245.48,1521.5599999999868 +2017-03-16,-24401.909999999996,23883.16,24311.28,24299.58,24314.92,1232.779999999988 +2017-03-17,24378.480000000003,-23864.68,-24472.0,-24498.25,24360.0,958.0699999999888 +2017-03-18,24378.480000000003,-23864.68,-24472.0,-24498.25,24426.36,958.0699999999888 +2017-03-19,24378.480000000003,-23864.68,-24472.0,-24498.25,24426.36,958.0699999999888 +2017-03-20,24378.480000000003,23995.16,24489.5,24472.58,-24301.48,1240.989999999987 +2017-03-21,-24496.239999999998,-24452.800000000003,-24516.269999999997,-24472.0,-24301.48,-728.5900000000111 +2017-03-22,24107.850000000002,23745.68,24149.07,24182.819999999996,-23803.92,1671.4599999999846 +2017-03-23,-24254.64,-23726.64,-24278.22,-24238.239999999998,-23794.399999999998,1107.32999999998 +2017-03-24,24143.4,-23726.64,24138.48,-24238.239999999998,-23510.2,1530.0299999999806 +2017-03-25,24143.4,-23677.08,24138.48,-24190.079999999998,-23383.92,1530.0299999999806 +2017-03-26,24143.4,-23677.08,24138.48,-24190.079999999998,-23383.92,1530.0299999999806 +2017-03-27,24143.4,23710.960000000003,-24135.039999999997,24090.48,23478.28,1670.9099999999817 +2017-03-28,24490.2,23710.960000000003,24240.96,24090.48,23478.28,1812.66999999998 +2017-03-29,24678.899999999998,23968.0,24240.96,24589.800000000003,23537.64,1812.66999999998 +2017-03-30,24899.9,24480.960000000003,-24779.34,-24755.960000000003,-24634.920000000002,392.02999999998065 +2017-03-31,-24832.08,24537.52,-24779.34,-24755.960000000003,-24634.920000000002,687.6499999999796 +2017-04-01,-24832.08,24823.12,-24716.7,-24709.52,-24586.199999999997,687.6499999999796 +2017-04-02,-24832.08,24823.12,-24716.7,-24709.52,-24586.199999999997,687.6499999999796 +2017-04-03,-24832.08,24823.12,24756.72,24860.1,24845.75,543.9499999999789 +2017-04-04,-24682.559999999998,24962.28,-24802.75,24860.1,-24724.530000000002,402.2199999999757 +2017-04-05,-24444.0,25391.24,24823.75,-24915.460000000003,-24724.530000000002,402.2199999999757 +2017-04-06,24867.02,-24253.559999999998,-24845.92,-24915.460000000003,-24618.39,440.62999999997555 +2017-04-07,-24614.920000000002,-24253.559999999998,-24845.92,-24853.18,-24507.755,583.7399999999761 +2017-04-08,-24614.920000000002,-24161.76,-24777.28,-24797.82,-24420.9,583.7399999999761 +2017-04-09,-24614.920000000002,-24161.76,-24777.28,-24797.82,-24420.9,583.7399999999761 +2017-04-10,24598.35,24490.079999999998,24540.96,-24797.82,-24420.9,1009.6699999999764 +2017-04-11,24598.35,-24363.72,-24625.92,-24768.409999999996,-24409.300000000003,729.8299999999763 +2017-04-12,-24594.930000000004,-24363.72,-24625.92,24531.4,24402.34,729.8299999999763 +2017-04-13,-24594.930000000004,-24198.21,-24566.08,-24542.7,-24365.219999999998,588.779999999977 +2017-04-14,-24439.319999999996,-23886.09,-24532.64,-24542.7,-24365.219999999998,588.779999999977 +2017-04-15,-24439.319999999996,-23886.09,-24532.64,-24542.7,-24365.219999999998,588.779999999977 +2017-04-16,-24439.319999999996,-23886.09,-24532.64,-24542.7,-24365.219999999998,588.779999999977 +2017-04-17,24443.5,24353.73,24465.659999999996,24394.760000000002,23943.64,2888.07999999998 +2017-04-18,-24801.280000000002,24353.73,-24808.960000000003,-24851.199999999997,-24765.71,42.88999999998123 +2017-04-19,-24801.280000000002,-24278.4,24612.710000000003,-24851.199999999997,23982.28,1326.209999999981 +2017-04-20,24565.32,24355.62,24612.710000000003,24499.68,23982.28,1754.789999999979 +2017-04-21,24565.32,-24260.309999999998,-24712.960000000003,-24754.980000000003,-24050.600000000002,1613.9299999999748 +2017-04-22,24859.38,-24260.309999999998,-24712.960000000003,-24754.980000000003,-24050.600000000002,1613.9299999999748 +2017-04-23,24859.38,-24260.309999999998,-24712.960000000003,-24754.980000000003,-24050.600000000002,1613.9299999999748 +2017-04-24,24859.38,24500.07,24729.9,24849.719999999998,24610.039999999997,2048.509999999973 +2017-04-25,25026.420000000002,24500.07,24729.9,24849.719999999998,24610.039999999997,2048.509999999973 +2017-04-26,-25528.899999999998,24505.74,24903.300000000003,-25426.191600000002,24887.52,2074.586799999972 +2017-04-27,25411.280000000002,24550.829999999998,24915.2,25450.829999999998,24895.92,2686.9067999999716 +2017-04-28,-25569.6,24806.52,25108.999999999996,-25569.7,24960.32,2238.8567999999686 +2017-04-29,-25569.6,24974.73,25542.5,-25569.7,25886.559999999998,2238.8567999999686 +2017-04-30,-25569.6,24974.73,25542.5,-25569.7,25886.559999999998,2238.8567999999686 +2017-05-01,25943.45,24974.73,25542.5,25948.2,25886.559999999998,2540.8067999999657 +2017-05-02,25943.45,-25560.359999999997,25918.2,25948.2,26118.960000000003,2540.8067999999657 +2017-05-03,-26450.3,-25560.359999999997,-26413.2,-26470.8,26238.52,1025.656799999968 +2017-05-04,26418.0,-25407.809999999998,-26413.2,-26470.8,26556.600000000002,1340.156799999968 +2017-05-05,-26465.399999999998,-25313.309999999998,-26247.899999999998,26365.920000000002,-25657.559999999998,2580.716799999973 +2017-05-06,-26465.399999999998,-25222.05,-26141.760000000002,26365.920000000002,-25657.559999999998,2580.716799999973 +2017-05-07,-26465.399999999998,-25222.05,-26141.760000000002,26365.920000000002,-25657.559999999998,2580.716799999973 +2017-05-08,-26465.399999999998,25624.079999999998,26435.5,26365.920000000002,25884.63,2429.6567999999716 +2017-05-09,26768.2,25624.079999999998,-26634.96,27081.0,-25831.170000000002,1971.2367999999733 +2017-05-10,26768.2,-26570.600000000002,-26634.96,-26820.5,-25831.170000000002,1328.8067999999694 +2017-05-11,-26793.26,-26570.600000000002,-26601.329999999998,26787.3,26764.92,685.4067999999679 +2017-05-12,26694.46,25956.45,26758.74,26787.3,-26743.92,1978.8567999999686 +2017-05-13,26694.46,25956.45,26758.74,27161.399999999998,-26743.92,1978.8567999999686 +2017-05-14,26694.46,25956.45,26758.74,27161.399999999998,-26743.92,1978.8567999999686 +2017-05-15,-27043.38,-26823.16,-27034.2,-26936.1,26858.16,396.14679999997315 +2017-05-16,-27043.38,26083.890000000003,-27034.2,-26936.1,26858.16,1362.2167999999729 +2017-05-17,-26940.29,-26446.559999999998,-26960.4,-26896.31,-26380.76,417.69679999997607 +2017-05-18,26157.6,25879.23,26135.82,26236.879999999997,25663.5,3077.906799999979 +2017-05-19,26157.6,25879.23,26135.82,26236.879999999997,25663.5,3077.906799999979 +2017-05-20,26379.36,25915.68,26206.62,26309.120000000003,25775.55,3077.906799999979 +2017-05-21,26379.36,25915.68,26206.62,26309.120000000003,25775.55,3077.906799999979 +2017-05-22,26379.36,25915.68,26206.62,26309.120000000003,25775.55,3077.906799999979 +2017-05-23,26402.88,26208.9,-26800.67,-26761.2,26029.890000000003,2178.0267999999814 +2017-05-24,-26817.5,26231.579999999998,26857.16,-26761.2,26204.85,2162.6067999999796 +2017-05-25,27066.300000000003,26469.45,26857.16,26927.25,26395.47,2660.9367999999813 +2017-05-26,-27450.670000000002,26821.26,27200.84,-27342.58,26780.22,1712.8167999999823 +2017-05-27,-27450.670000000002,26886.059999999998,27231.27,-27342.58,26818.29,1712.8167999999823 +2017-05-28,-27450.670000000002,26886.059999999998,27231.27,-27342.58,26818.29,1712.8167999999823 +2017-05-29,-27450.670000000002,26886.059999999998,27231.27,-27342.58,26818.29,1712.8167999999823 +2017-05-30,27420.96,26886.059999999998,27231.27,27353.26,26818.29,1876.0367999999835 +2017-05-31,-27395.76,-26854.74,-27414.260000000002,-27496.8,-26651.43,1267.5967999999812 +2017-06-01,-27395.76,26890.65,27275.4,27266.04,26683.829999999998,1725.4867999999806 +2017-06-02,27419.88,26890.65,27275.4,27266.04,26683.829999999998,2055.846799999981 +2017-06-03,27419.88,27181.71,27649.800000000003,27670.1,26895.24,2055.846799999981 +2017-06-04,27419.88,27181.71,27649.800000000003,27670.1,26895.24,2055.846799999981 +2017-06-05,-27730.08,27181.71,27649.800000000003,-27707.4,26895.24,1417.866799999978 +2017-06-06,27748.559999999998,-27080.190000000002,-27658.61,27646.55,-26910.359999999997,1419.5067999999737 +2017-06-07,27748.559999999998,27271.890000000003,27561.600000000002,27646.55,27042.93,1572.6267999999727 +2017-06-08,27822.480000000003,-27268.38,27561.600000000002,-27743.210000000003,27042.93,1572.6267999999727 +2017-06-09,-27813.28,-27268.38,-27831.18,-27743.210000000003,-27163.36,-1559.5132000000267 +2017-06-10,-27813.28,-26414.37,-27831.18,-26667.42,-27163.36,-1559.5132000000267 +2017-06-11,-27813.28,-26414.37,-27831.18,-26667.42,-27163.36,-1559.5132000000267 +2017-06-12,-27813.28,-26414.37,-27831.18,-26667.42,-27163.36,-1559.5132000000267 +2017-06-13,26267.84,25500.54,26218.32,26239.61,26203.5,2810.8167999999714 +2017-06-14,-26634.999999999996,-26364.690000000002,-26594.25,-26564.28,-26134.109999999997,346.3567999999723 +2017-06-15,-26634.999999999996,-26364.690000000002,-26594.25,-26564.28,-26134.109999999997,346.3567999999723 +2017-06-16,26209.36,25680.46,26211.359999999997,-26405.07,-25924.859999999997,2243.1267999999764 +2017-06-17,26209.36,25680.46,26211.359999999997,-26035.410000000003,-25882.74,2243.1267999999764 +2017-06-18,26209.36,25680.46,26211.359999999997,-26035.410000000003,-25882.74,2243.1267999999764 +2017-06-19,26209.36,25680.46,26211.359999999997,26341.2,26330.940000000002,2682.146799999977 +2017-06-20,-26760.800000000003,-26799.93,-26796.0,-26826.85,-26162.73,51.80679999997665 +2017-06-21,26665.16,26057.98,26626.43,26548.34,26421.93,2573.4967999999753 +2017-06-22,-26950.859999999997,-26033.8,-26845.0,-26941.55,-26368.74,1520.0267999999778 +2017-06-23,26865.1,26097.24,26827.11,26928.399999999998,26624.43,2608.636799999982 +2017-06-24,26865.1,26097.24,26827.11,26928.399999999998,26624.43,2608.636799999982 +2017-06-25,26865.1,26097.24,26827.11,26928.399999999998,26624.43,2608.636799999982 +2017-06-26,-27090.0,-26837.46,-27185.43,-27122.52,-26246.43,393.65679999997883 +2017-06-27,-27090.0,-26837.46,-27185.43,-27122.52,-26246.43,393.65679999997883 +2017-06-28,26233.11,25748.58,26204.04,26249.4,25947.27,3331.8167999999787 +2017-06-29,-26716.02,-26350.109999999997,-26734.079999999998,-26724.48,-26258.960000000003,-1400.8832000000148 +2017-06-30,-26716.02,-26350.109999999997,-26734.079999999998,26211.640000000003,-26258.960000000003,-824.8032000000167 +2017-07-01,-26594.98,-26136.0,-26723.46,26211.640000000003,-26031.039999999997,-824.8032000000167 +2017-07-02,-26594.98,-26136.0,-26723.46,26211.640000000003,-26031.039999999997,-824.8032000000167 +2017-07-03,-26594.98,-26136.0,-26723.46,-26117.0,-26031.039999999997,-824.8032000000167 +2017-07-04,-26018.26,-25748.82,-26272.11,-26117.0,-25744.88,-824.8032000000167 +2017-07-05,25684.140000000003,25256.399999999998,25708.14,25792.11,25171.02,3003.606799999983 +2017-07-06,-26032.5,-26058.78,-26043.5,-26119.589999999997,-25975.32,-640.4232000000156 +2017-07-07,25830.960000000003,25447.76,25896.239999999998,25808.22,25401.87,3362.706799999989 +2017-07-08,25830.960000000003,25447.76,25896.239999999998,25808.22,25401.87,3362.706799999989 +2017-07-09,25830.960000000003,25447.76,25896.239999999998,25808.22,25401.87,3362.706799999989 +2017-07-10,25830.960000000003,25447.76,25896.239999999998,25808.22,25401.87,3362.706799999989 +2017-07-11,26259.239999999998,-25847.38,26248.5,25965.74,25677.0,3362.706799999989 +2017-07-12,26544.760000000002,26169.26,26551.170000000002,26049.87,25745.309999999998,3362.706799999989 +2017-07-13,-27205.239999999998,-26996.085,27171.9,26087.460000000003,26126.82,2362.8517999999895 +2017-07-14,27229.280000000002,27048.87,27233.46,26450.83,26155.372499999998,2846.2117999999864 +2017-07-15,27229.280000000002,27048.87,27354.87,26678.16,26376.57,2846.2117999999864 +2017-07-16,27229.280000000002,27048.87,27354.87,26678.16,26376.57,2846.2117999999864 +2017-07-17,27229.280000000002,27048.87,-27473.559999999998,26678.16,-27326.88,1710.5217999999877 +2017-07-18,27327.3,27271.079999999998,27523.340000000004,26771.24,26647.65,3186.0517999999865 +2017-07-19,31028.399999999998,27658.260000000002,27523.340000000004,26864.320000000003,26647.65,3186.0517999999865 +2017-07-20,-28641.6,27725.489999999998,27739.659999999996,-28564.600000000002,-27781.32,2926.921799999982 +2017-07-21,28658.079999999998,-27693.090000000004,-28610.82,-28564.600000000002,27827.52,2858.931799999984 +2017-07-22,28658.079999999998,-27693.090000000004,-28610.82,-28551.300000000003,27827.52,2858.931799999984 +2017-07-23,28658.079999999998,-27693.090000000004,-28610.82,-28551.300000000003,27827.52,2858.931799999984 +2017-07-24,-28750.23,28051.65,28718.0,28745.010000000002,27827.52,2989.111799999984 +2017-07-25,-28750.23,28051.65,-28924.0,28745.010000000002,-28101.87,1689.521799999984 +2017-07-26,28740.160000000003,28076.489999999998,28816.140000000003,28867.86,-28101.87,2044.2117999999828 +2017-07-27,-28863.440000000002,-28242.0,28816.140000000003,-28907.52,-27993.989999999998,496.4517999999807 +2017-07-28,28710.239999999998,-28242.0,29656.56,-28907.52,28749.9,-93.79820000001928 +2017-07-29,28710.239999999998,-27541.079999999998,30006.3,-28704.0,28749.9,-93.79820000001928 +2017-07-30,28710.239999999998,-27541.079999999998,30006.3,-28704.0,28749.9,-93.79820000001928 +2017-07-31,-28702.28,-27541.079999999998,-28603.25,-28704.0,-28365.0,389.131799999981 +2017-08-01,28214.65,27893.32,28196.760000000002,28209.4,27450.239999999998,1995.3717999999863 +2017-08-02,-28376.18,-27884.92,-28264.75,28209.4,27450.239999999998,1464.6417999999867 +2017-08-03,-28376.18,-27884.92,-28264.75,-28469.309999999998,-28209.0,1302.191799999986 +2017-08-04,28302.390000000003,27652.24,28326.54,28306.589999999997,28373.699999999997,1614.9717999999884 +2017-08-05,28302.390000000003,27652.24,28326.54,28306.589999999997,28373.699999999997,1614.9717999999884 +2017-08-06,28302.390000000003,27652.24,28326.54,28306.589999999997,28373.699999999997,1614.9717999999884 +2017-08-07,28302.390000000003,27652.24,28326.54,28306.589999999997,-28372.5,1614.9717999999884 +2017-08-08,-28537.600000000002,27652.24,-28595.41,28306.589999999997,-28372.5,1079.8917999999867 +2017-08-09,-28537.600000000002,-28478.29,-28595.41,28974.480000000003,-28325.7,97.88179999998465 +2017-08-10,-28124.8,-28478.29,-28587.06,-28414.410000000003,-28202.4,-212.65820000001622 +2017-08-11,27595.4,27103.72,27733.2,27716.48,26972.61,2952.5417999999772 +2017-08-12,27595.4,27103.72,27733.2,27716.48,26972.61,2952.5417999999772 +2017-08-13,27595.4,27103.72,27733.2,27716.48,26972.61,2952.5417999999772 +2017-08-14,-27873.0,27103.72,27733.2,27716.48,26972.61,2610.5417999999772 +2017-08-15,-27873.0,-27516.72,28173.75,28133.6,-28142.4,1672.4617999999755 +2017-08-16,28216.679999999997,-27516.72,-28220.0,-28166.249999999996,27383.829999999998,2097.741799999978 +2017-08-17,-28235.3,-27389.039999999997,-28220.0,-28166.249999999996,-27829.8,505.72179999998116 +2017-08-18,27645.64,-26895.960000000003,27622.649999999998,-27627.25,-27829.8,1339.2917999999809 +2017-08-19,27645.64,-26837.16,27622.649999999998,-27562.5,-27785.399999999998,1339.2917999999809 +2017-08-20,27645.64,-26837.16,27622.649999999998,-27562.5,-27785.399999999998,1339.2917999999809 +2017-08-21,27645.64,-26837.16,27622.649999999998,-27562.5,-27785.399999999998,1339.2917999999809 +2017-08-22,27682.16,27073.2,27683.7,27641.94,27271.6,2599.2517999999836 +2017-08-23,-28063.96,-27782.0,-28005.86,27641.94,27271.6,1472.5417999999845 +2017-08-24,-28063.96,-27782.0,-28005.86,-28031.52,-27169.81,994.7317999999868 +2017-08-25,-27909.579999999998,-27621.050000000003,-27844.84,27815.640000000003,-27169.81,1314.4517999999844 +2017-08-26,-27547.699999999997,-27412.54,-27609.12,27815.640000000003,-26984.5,1314.4517999999844 +2017-08-27,-27547.699999999997,-27412.54,-27609.12,27815.640000000003,-26984.5,1314.4517999999844 +2017-08-28,27574.8,27434.579999999998,27594.600000000002,27815.640000000003,-26984.5,1648.811799999985 +2017-08-29,27574.8,27434.579999999998,27594.600000000002,28095.78,27136.75,1648.811799999985 +2017-08-30,27853.65,27667.739999999998,27728.250000000004,28346.34,27136.75,1648.811799999985 +2017-08-31,28823.85,28060.11,28036.8,28422.899999999998,27365.27,1648.811799999985 +2017-09-01,28827.15,-28369.25,28380.0,28536.0,-28559.7,696.8217999999833 +2017-09-02,28832.100000000002,-28369.25,28383.300000000003,28544.7,-28559.7,696.8217999999833 +2017-09-03,28832.100000000002,-28369.25,28383.300000000003,28544.7,-28559.7,696.8217999999833 +2017-09-04,28832.100000000002,-28369.25,28383.300000000003,28544.7,-28559.7,696.8217999999833 +2017-09-05,-28621.280000000002,-28369.25,-28510.24,-28526.08,-28559.7,205.7417999999816 +2017-09-06,28321.5,28066.199999999997,28394.850000000002,-28526.08,28260.6,1625.4217999999782 +2017-09-07,-28461.0,28066.199999999997,28394.850000000002,-28496.16,28260.6,1446.4217999999782 +2017-09-08,-28461.0,-28011.1,-28719.6,-28381.76,-28242.3,933.5717999999761 +2017-09-09,-28050.78,-28011.1,-28719.6,-27918.879999999997,-28242.3,933.5717999999761 +2017-09-10,-28050.78,-28011.1,-28719.6,-27918.879999999997,-28242.3,933.5717999999761 +2017-09-11,28351.440000000002,28360.84,28282.129999999997,28262.5,28298.699999999997,2507.8417999999765 +2017-09-12,28351.440000000002,28360.84,-28711.36,-28786.78,28298.699999999997,1345.6817999999766 +2017-09-13,-28831.48,28494.82,28899.350000000002,-28786.78,28399.5,988.9917999999743 +2017-09-14,-28831.48,-28774.09,-28892.24,-28577.350000000002,-28203.9,647.0717999999724 +2017-09-15,-28672.91,-28774.09,28653.86,28618.52,-28203.9,990.2317999999723 +2017-09-16,-28589.7,-28616.91,28653.86,28618.52,-28058.699999999997,990.2317999999723 +2017-09-17,-28589.7,-28616.91,28653.86,28618.52,-28058.699999999997,990.2317999999723 +2017-09-18,28616.100000000002,-28616.91,-28561.68,-28560.6,-28058.699999999997,1030.7917999999663 +2017-09-19,28616.100000000002,-28251.510000000002,28465.800000000003,28571.399999999998,28105.8,1548.351799999964 +2017-09-20,-28568.539999999997,28223.09,-28580.219999999998,-28716.879999999997,28105.8,937.4117999999689 +2017-09-21,28505.78,-27974.85,-28580.219999999998,-28716.879999999997,28426.199999999997,1503.751799999969 +2017-09-22,-28477.2,-27974.85,-28404.260000000002,-28223.76,-28297.8,1316.401799999967 +2017-09-23,-28477.2,-27697.9,-28309.64,-27947.76,-28297.8,1316.401799999967 +2017-09-24,-28477.2,-27697.9,-28309.64,-27947.76,-28297.8,1316.401799999967 +2017-09-25,-28477.2,-27697.9,-28309.64,-27947.76,-28297.8,1316.401799999967 +2017-09-26,27624.52,-27253.91,27587.280000000002,27565.199999999997,27185.469999999998,2179.2117999999646 +2017-09-27,27624.52,27575.23,27587.280000000002,27565.199999999997,27185.469999999998,2179.2117999999646 +2017-09-28,-28189.199999999997,27575.23,28170.24,-28203.52,27837.1,1204.6917999999678 +2017-09-29,28290.6,27735.6,28346.64,28203.96,27979.489999999998,1358.8117999999704 +2017-09-30,28290.6,27879.15,28696.08,28203.96,28237.88,1358.8117999999704 +2017-10-01,28290.6,27879.15,28696.08,28203.96,28237.88,1358.8117999999704 +2017-10-02,-28498.609999999997,-27816.510000000002,-28470.96,-28454.850000000002,-28056.63,166.1417999999685 +2017-10-03,28132.829999999998,-27816.510000000002,28213.36,28269.839999999997,28190.32,1531.7817999999752 +2017-10-04,28132.829999999998,27998.050000000003,-28294.559999999998,-28388.397999999997,-28036.62,888.040199999974 +2017-10-05,28958.649999999998,27998.050000000003,28425.84,28436.37,27585.32,2526.4901999999747 +2017-10-06,30519.23,28444.65,28425.84,-29041.100000000002,27585.32,1905.290199999974 +2017-10-07,31089.140000000003,28697.82,28590.179999999997,-29041.100000000002,27821.92,1905.290199999974 +2017-10-08,31089.140000000003,28697.82,28590.179999999997,-29041.100000000002,27821.92,1905.290199999974 +2017-10-09,-29333.63,28697.82,28590.179999999997,29297.920000000002,-28776.989999999998,2332.1001999999717 +2017-10-10,-29333.63,-28628.800000000003,-29341.89,29297.920000000002,-28776.989999999998,1474.1501999999746 +2017-10-11,-29066.920000000002,28855.0,29193.06,29309.2,29163.85,1819.6301999999741 +2017-10-12,29379.000000000004,28855.0,-29333.500000000004,-29484.0,29163.85,1295.2201999999706 +2017-10-13,29379.000000000004,29026.969999999998,29535.800000000003,29514.120000000003,29163.85,1452.2101999999686 +2017-10-14,29923.5,29085.260000000002,29535.800000000003,29514.120000000003,29228.23,1452.2101999999686 +2017-10-15,29923.5,29085.260000000002,29535.800000000003,29514.120000000003,29228.23,1452.2101999999686 +2017-10-16,29923.5,29085.260000000002,29535.800000000003,29514.120000000003,29228.23,1452.2101999999686 +2017-10-17,-29922.0,29183.86,29668.4,30057.44,29271.15,1452.2101999999686 +2017-10-18,-29922.0,-29910.0,-29925.1,-29875.12,29319.0,614.970199999967 +2017-10-19,-29331.0,-29910.0,-29925.1,-29875.12,-29053.36,614.970199999967 +2017-10-20,-29269.5,-29598.3,29396.64,29358.079999999998,29147.030000000002,808.7701999999663 +2017-10-21,-29124.0,-29487.3,29396.64,29358.079999999998,29147.030000000002,808.7701999999663 +2017-10-22,-29124.0,-29487.3,29396.64,29358.079999999998,29147.030000000002,808.7701999999663 +2017-10-23,-29124.0,-29487.3,-29458.440000000002,29358.079999999998,-28580.66,123.69019999996453 +2017-10-24,29010.960000000003,28301.1,29034.2,29359.96,28666.21,2007.0301999999647 +2017-10-25,-29259.27,-29187.3,-29172.6,-29247.735,28666.21,268.01519999996526 +2017-10-26,29086.29,-29187.3,29007.1,29120.85,-28751.18,1143.8851999999679 +2017-10-27,29086.29,28624.7,29007.1,29120.85,28942.760000000002,6581.355199999969 +2017-10-28,29731.46,28624.7,30239.6,30164.250000000004,28942.760000000002,6581.355199999969 +2017-10-29,29731.46,28624.7,30239.6,30164.250000000004,28942.760000000002,6581.355199999969 +2017-10-30,-30747.350000000002,28624.7,30239.6,30164.250000000004,-29960.770000000004,4358.005199999963 +2017-10-31,-30747.350000000002,-30947.84,30577.9,30843.2,-29960.770000000004,2147.445199999962 +2017-11-01,30888.0,-30947.84,30610.2,-31041.539999999997,30235.399999999998,1782.5551999999552 +2017-11-02,30888.0,-30903.04,-31132.079999999998,31100.350000000002,30235.399999999998,1234.9851999999555 +2017-11-03,31093.92,30013.199999999997,-31132.079999999998,31100.350000000002,30246.13,2346.585199999954 +2017-11-04,31201.559999999998,30013.199999999997,-31132.079999999998,31912.5,30449.71,2346.585199999954 +2017-11-05,31201.559999999998,30013.199999999997,-31132.079999999998,31912.5,30449.71,2346.585199999954 +2017-11-06,31201.559999999998,30013.199999999997,31349.579999999998,31912.5,-31280.4,1303.9051999999538 +2017-11-07,-31342.399999999998,30257.820000000003,31349.579999999998,32236.25,30519.31,1572.7351999999555 +2017-11-08,-31342.399999999998,30325.590000000004,31363.5,32339.850000000002,30519.31,1572.7351999999555 +2017-11-09,-31342.399999999998,30325.590000000004,-31377.500000000004,32339.850000000002,-31431.600000000002,345.71519999995144 +2017-11-10,-31024.0,-30384.449999999997,-31377.500000000004,-31440.6,-31431.600000000002,1219.06519999995 +2017-11-11,-30723.2,-30384.449999999997,-31230.5,-31440.6,-31324.500000000004,1219.06519999995 +2017-11-12,-30723.2,-30384.449999999997,-31230.5,-31440.6,-31324.500000000004,1219.06519999995 +2017-11-13,31212.800000000003,30487.590000000004,31105.980000000003,-31440.6,-31324.500000000004,1397.8351999999468 +2017-11-14,31212.800000000003,30487.590000000004,-31340.32,-31314.6,31249.200000000004,1041.6951999999474 +2017-11-15,-31123.440000000002,-30420.63,-31340.32,-30841.2,-31092.300000000003,657.4551999999458 +2017-11-16,30890.579999999998,30706.829999999998,30889.48,30969.1,30405.63,3035.225199999946 +2017-11-17,-31298.399999999998,-30506.760000000002,-31325.0,-31307.600000000002,-31076.700000000004,179.08519999994314 +2017-11-18,-31298.399999999998,-30506.760000000002,-31325.0,-31307.600000000002,-31076.700000000004,179.08519999994314 +2017-11-19,-31298.399999999998,-30506.760000000002,-31325.0,-31307.600000000002,-31076.700000000004,179.08519999994314 +2017-11-20,31056.0,-30506.760000000002,-31325.0,-31307.600000000002,-31076.700000000004,567.2851999999439 +2017-11-21,31056.0,30766.23,31098.06,30992.059999999998,30458.699999999997,3210.72519999995 +2017-11-22,31396.8,30766.23,-31471.38,30992.059999999998,30458.699999999997,2668.1151999999456 +2017-11-23,31411.199999999997,31216.320000000003,-31471.38,31317.84,30505.68,2668.1151999999456 +2017-11-24,-31711.5,31216.320000000003,31620.94,31317.84,30505.68,2459.3951999999445 +2017-11-25,-31711.5,32022.0,31620.94,31319.63,30639.079999999998,2459.3951999999445 +2017-11-26,-31711.5,32022.0,31620.94,31319.63,30639.079999999998,2459.3951999999445 +2017-11-27,-31711.5,32022.0,31620.94,-31858.47,30639.079999999998,1763.0351999999402 +2017-11-28,31868.800000000003,-31033.6,-31923.499999999996,-31858.47,-31898.699999999997,1926.865199999942 +2017-11-29,-31985.5,-31033.6,-31923.499999999996,-31671.809999999998,-31898.699999999997,45.36519999994198 +2017-11-30,-31985.5,30595.5,30829.32,30933.0,-31121.4,738.0951999999415 +2017-12-01,-31888.600000000002,-30221.1,-31167.8,-31131.100000000002,-31085.100000000002,-304.40480000005846 +2017-12-02,-31759.399999999998,-30221.1,-31167.8,-31131.100000000002,-30752.1,-304.40480000005846 +2017-12-03,-31759.399999999998,-30221.1,-31167.8,-31131.100000000002,-30752.1,-304.40480000005846 +2017-12-04,-31759.399999999998,-30221.1,-31167.8,-31131.100000000002,-30752.1,-304.40480000005846 +2017-12-05,30394.65,29680.82,30418.08,-30903.600000000002,29568.4,1981.9051999999392 +2017-12-06,30394.65,29680.82,30418.08,-30874.479999999996,29568.4,1981.9051999999392 +2017-12-07,-30743.199999999997,29961.1,30986.56,30840.264,29948.88,1796.7051999999385 +2017-12-08,30920.559999999998,30154.54,-30967.0,-30994.71,30292.53,2541.4151999999376 +2017-12-09,30920.559999999998,30212.0,-30967.0,-30994.71,30432.020000000004,2541.4151999999376 +2017-12-10,30920.559999999998,30212.0,-30967.0,-30994.71,30432.020000000004,2541.4151999999376 +2017-12-11,-31098.74,30212.0,31152.96,31080.6,30432.020000000004,2321.7251999999207 +2017-12-12,-31098.74,-30292.079999999998,-31144.960000000003,-31249.399999999998,-30414.329999999998,1624.4051999999174 +2017-12-13,30996.9,-30292.079999999998,31024.2,31008.600000000002,30490.31,2701.265199999918 +2017-12-14,30996.9,30530.76,31024.2,-31171.82,30490.31,2529.045199999913 +2017-12-15,31277.4,30530.76,31039.859999999997,31296.600000000002,30666.63,2702.915199999912 +2017-12-16,31369.8,30657.640000000003,31351.32,31296.600000000002,31088.0,2702.915199999912 +2017-12-17,31369.8,30657.640000000003,31351.32,31296.600000000002,31088.0,2702.915199999912 +2017-12-18,31369.8,30657.640000000003,31351.32,31296.600000000002,31088.0,2702.915199999912 +2017-12-19,-31793.4,-30871.880000000005,-31773.269999999997,-31940.82,-31313.62,705.6651999999158 +2017-12-20,31532.94,-30871.880000000005,-31773.269999999997,-31940.82,-31313.62,1272.1251999999113 +2017-12-21,-31499.54,-30618.119999999995,-31486.53,31501.8,-31133.239999999998,1797.15519999991 +2017-12-22,31530.04,-30543.76,-31408.649999999998,31501.8,-31054.649999999998,1987.0951999999088 +2017-12-23,31530.04,-30377.359999999997,-31364.399999999998,31501.8,-30996.94,1987.0951999999088 +2017-12-24,31530.04,-30377.359999999997,-31364.399999999998,31501.8,-30996.94,1987.0951999999088 +2017-12-25,31530.04,-30377.359999999997,-31364.399999999998,31501.8,-30996.94,1987.0951999999088 +2017-12-26,-31543.68,30595.76,-31364.399999999998,-31384.879999999997,-30996.94,929.2951999999132 +2017-12-27,-31543.68,30595.76,31261.120000000003,31219.8,-30909.649999999998,1277.5151999999107 +2017-12-28,31219.02,30738.76,31261.120000000003,31219.8,-30745.800000000003,2433.7751999999127 +2017-12-29,-31481.440000000002,-30406.22,-31409.88,-31476.78,-30622.550000000003,1189.2451999999066 diff --git a/alphapy/examples/Trading System/systems/faang_closer_returns_1d.csv b/alphapy/examples/Trading System/systems/faang_closer_returns_1d.csv new file mode 100644 index 0000000..6f03aea --- /dev/null +++ b/alphapy/examples/Trading System/systems/faang_closer_returns_1d.csv @@ -0,0 +1,499 @@ +date,return +2016-08-19,2.220446049250313e-16 +2016-08-20,0.0 +2016-08-21,0.0 +2016-08-22,-0.0019766999999999424 +2016-08-23,0.0030824931642376985 +2016-08-24,-0.006606534793687335 +2016-08-25,0.004267933813842717 +2016-08-26,0.004104912006775097 +2016-08-27,0.0 +2016-08-28,0.0 +2016-08-29,0.0029717898347676464 +2016-08-30,-0.004242947625705096 +2016-08-31,0.0005226936927096304 +2016-09-01,0.0018824107511470345 +2016-09-02,0.004241137328821587 +2016-09-03,0.0 +2016-09-04,0.0 +2016-09-05,0.0 +2016-09-06,0.017502300859255193 +2016-09-07,0.00027440490446850596 +2016-09-08,-0.006769168819236615 +2016-09-09,-0.025412981679501456 +2016-09-10,0.0 +2016-09-11,0.0 +2016-09-12,0.018015063058056446 +2016-09-13,-0.008430107560909783 +2016-09-14,0.010426457282463053 +2016-09-15,0.013480305789569602 +2016-09-16,0.005639420860773292 +2016-09-17,0.0 +2016-09-18,0.0 +2016-09-19,-0.007263289147146024 +2016-09-20,0.0027255893712647694 +2016-09-21,-0.0011455878183785906 +2016-09-22,0.01051682068915194 +2016-09-23,-0.006272527618697787 +2016-09-24,0.0 +2016-09-25,0.0 +2016-09-26,-0.008229443196589137 +2016-09-27,0.014031604974783951 +2016-09-28,0.00604186773996207 +2016-09-29,-0.00814507886665472 +2016-09-30,0.008083413879760304 +2016-10-01,0.0 +2016-10-02,0.0 +2016-10-03,0.00699909709173796 +2016-10-04,-0.0006433627237524275 +2016-10-05,0.010194007701313401 +2016-10-06,-0.0005249523548678248 +2016-10-07,-0.000900417946041121 +2016-10-08,0.0 +2016-10-09,0.0 +2016-10-10,0.006416560860518228 +2016-10-11,-0.01044999782276057 +2016-10-12,0.0012879890868506827 +2016-10-13,-0.004099858636995468 +2016-10-14,0.002309351752352029 +2016-10-15,0.0 +2016-10-16,0.0 +2016-10-17,-0.005813783023897678 +2016-10-18,0.04448440251392238 +2016-10-19,0.008000097224514224 +2016-10-20,-0.000854750209245636 +2016-10-21,0.011750825450213886 +2016-10-22,0.0 +2016-10-23,0.0 +2016-10-24,0.010394856959407894 +2016-10-25,-0.00405634110825337 +2016-10-26,-0.01012538950575026 +2016-10-27,-0.006941592388168161 +2016-10-28,-0.008080147491633216 +2016-10-29,0.0 +2016-10-30,0.0 +2016-10-31,-0.0025707081840423696 +2016-11-01,-0.010626095650896006 +2016-11-02,-0.014209407893487791 +2016-11-03,-0.015976671629929773 +2016-11-04,-0.004181342482462491 +2016-11-05,0.0 +2016-11-06,0.0 +2016-11-07,0.02265819983831796 +2016-11-08,0.0072398313065400455 +2016-11-09,-0.010798396486940032 +2016-11-10,-0.03408446057934533 +2016-11-11,-0.005929192772749614 +2016-11-12,0.0 +2016-11-13,0.0 +2016-11-14,-0.0240985106979279 +2016-11-15,0.01905860710964924 +2016-11-16,0.008596798277447748 +2016-11-17,0.00614561990983109 +2016-11-18,-0.0023097805242427993 +2016-11-19,0.0 +2016-11-20,0.0 +2016-11-21,0.02319419921662491 +2016-11-22,0.0011481595727791394 +2016-11-23,-0.005409191349930231 +2016-11-24,0.0 +2016-11-25,0.00015408152699092348 +2016-11-26,0.0 +2016-11-27,0.0 +2016-11-28,-0.003155940198070528 +2016-11-29,0.0013643696774972014 +2016-11-30,-0.013061636195479731 +2016-12-01,-0.01187528617906941 +2016-12-02,0.0065905686375544725 +2016-12-03,0.0 +2016-12-04,0.0 +2016-12-05,0.00773607590649239 +2016-12-06,0.01121016269437769 +2016-12-07,0.009588193213542429 +2016-12-08,0.0003113223858890457 +2016-12-09,0.007822879165423124 +2016-12-10,0.0 +2016-12-11,0.0 +2016-12-12,-0.006951507759196618 +2016-12-13,0.014672817074697697 +2016-12-14,-0.0014977709709621534 +2016-12-15,0.0017010287997036855 +2016-12-16,-0.004364941158150004 +2016-12-17,0.0 +2016-12-18,0.0 +2016-12-19,0.0048083429526728505 +2016-12-20,0.0017250129676571468 +2016-12-21,0.0014187053985241782 +2016-12-22,-0.007143873249467747 +2016-12-23,-0.0017126973378087662 +2016-12-24,0.0 +2016-12-25,0.0 +2016-12-26,0.0 +2016-12-27,0.010158164531348213 +2016-12-28,-0.007661331559934648 +2016-12-29,-0.004101379717379117 +2016-12-30,-0.012695965520566843 +2016-12-31,0.0 +2017-01-01,0.0 +2017-01-02,0.0 +2017-01-03,0.014570289130705572 +2017-01-04,0.006632061265640532 +2017-01-05,0.015094060588947578 +2017-01-06,0.012337861552315976 +2017-01-07,0.0 +2017-01-08,0.0 +2017-01-09,0.004729199325819167 +2017-01-10,-0.0027987815932913884 +2017-01-11,0.0064499550046261955 +2017-01-12,0.0014723792068025432 +2017-01-13,0.010504455064903606 +2017-01-14,0.0 +2017-01-15,0.0 +2017-01-16,0.0 +2017-01-17,-0.0029214962337680284 +2017-01-18,0.000455135365362791 +2017-01-19,0.006033809453054095 +2017-01-20,0.0005905145644624632 +2017-01-21,0.0 +2017-01-22,0.0 +2017-01-23,0.007440266801403306 +2017-01-24,0.006700134859505491 +2017-01-25,0.01094978860139273 +2017-01-26,0.0015376517661256361 +2017-01-27,0.0004765379351068244 +2017-01-28,0.0 +2017-01-29,0.0 +2017-01-30,-0.010231947853548973 +2017-01-31,-0.004672318850449497 +2017-02-01,0.017826010856724528 +2017-02-02,-0.0035787935555101624 +2017-02-03,-0.0038864923990367117 +2017-02-04,0.0 +2017-02-05,0.0 +2017-02-06,0.004295394130674479 +2017-02-07,0.008889513220064149 +2017-02-08,0.007246373099900616 +2017-02-09,8.32292050092498e-05 +2017-02-10,0.003142530864066684 +2017-02-11,0.0 +2017-02-12,0.0 +2017-02-13,0.0024530266019398006 +2017-02-14,-0.0008060953449404717 +2017-02-15,0.0029909894986428487 +2017-02-16,0.001464504056134741 +2017-02-17,0.0016332393706037784 +2017-02-18,0.0 +2017-02-19,0.0 +2017-02-20,0.0 +2017-02-21,0.005560583152849796 +2017-02-22,0.00620803697560679 +2017-02-23,-0.004333106865295755 +2017-02-24,-0.0013927650776368594 +2017-02-25,0.0 +2017-02-26,0.0 +2017-02-27,0.003274400403654232 +2017-02-28,-0.004906866548296751 +2017-03-01,0.012242477034922095 +2017-03-02,-0.008940111256843464 +2017-03-03,0.0012761636215157246 +2017-03-04,0.0 +2017-03-05,0.0 +2017-03-06,0.0025815650148952596 +2017-03-07,0.00013170065411061138 +2017-03-08,-7.856247900761559e-05 +2017-03-09,0.0021460513326794217 +2017-03-10,0.00266371148983513 +2017-03-11,0.0 +2017-03-12,0.0 +2017-03-13,0.006189384611527071 +2017-03-14,-0.0013296145658867609 +2017-03-15,0.006231816583440075 +2017-03-16,6.770687816182175e-06 +2017-03-17,7.961622369823118e-05 +2017-03-18,0.0 +2017-03-19,0.0 +2017-03-20,0.0032733855669888623 +2017-03-21,-0.01615059941471153 +2017-03-22,0.0052774652300784375 +2017-03-23,-0.004406118866977726 +2017-03-24,-0.00043165490197905054 +2017-03-25,0.0 +2017-03-26,0.0 +2017-03-27,0.00423647226951851 +2017-03-28,0.01029891869167776 +2017-03-29,0.00976642372254477 +2017-03-30,0.0019544849547954435 +2017-03-31,0.0006864087920837036 +2017-04-01,0.0 +2017-04-02,0.0 +2017-04-03,0.0023810008212761247 +2017-04-04,0.0012722685095452047 +2017-04-05,-0.003702669538766745 +2017-04-06,-0.004662365612843056 +2017-04-07,-0.0033259387225614834 +2017-04-08,0.0 +2017-04-09,0.0 +2017-04-10,0.0037318690020351486 +2017-04-11,-0.004481945377835417 +2017-04-12,-0.0019392864766002438 +2017-04-13,-0.005430213377537685 +2017-04-14,0.0 +2017-04-15,0.0 +2017-04-16,0.0 +2017-04-17,0.01744615554361406 +2017-04-18,-0.006564978198540183 +2017-04-19,-0.004320618797610587 +2017-04-20,0.008098977925211193 +2017-04-21,0.0009434080060386485 +2017-04-22,0.0 +2017-04-23,0.0 +2017-04-24,0.012172826613235488 +2017-04-25,0.016393078946025375 +2017-04-26,-0.003400720336725782 +2017-04-27,0.00811064598967004 +2017-04-28,0.010567191070443904 +2017-04-29,0.0 +2017-04-30,0.0 +2017-05-01,0.017685664736846185 +2017-05-02,0.003569729706044855 +2017-05-03,-0.0016925099113830333 +2017-05-04,0.0007750651106837037 +2017-05-05,4.8932646528943025e-05 +2017-05-06,0.0 +2017-05-07,0.0 +2017-05-08,0.01094648272689458 +2017-05-09,0.0022373251704863595 +2017-05-10,0.0012437526673907406 +2017-05-11,-0.0016984802434362178 +2017-05-12,0.008742084707739517 +2017-05-13,0.0 +2017-05-14,0.0 +2017-05-15,-0.0014980727856830356 +2017-05-16,0.0011920840396626442 +2017-05-17,-0.029944950213551924 +2017-05-18,0.01485395807804446 +2017-05-19,0.0038550235720158366 +2017-05-20,0.0 +2017-05-21,0.0 +2017-05-22,0.005836265695866594 +2017-05-23,0.0019903404365635424 +2017-05-24,0.0049841169668429774 +2017-05-25,0.015315473054917872 +2017-05-26,-0.0001099981903551761 +2017-05-27,0.0 +2017-05-28,0.0 +2017-05-29,0.0 +2017-05-30,0.002125119436470868 +2017-05-31,-0.00474233744691277 +2017-06-01,0.001044203291957091 +2017-06-02,0.012015779183749853 +2017-06-03,0.0 +2017-06-04,0.0 +2017-06-05,0.0003392318417756446 +2017-06-06,-0.0032818242720080715 +2017-06-07,0.0044703388446067915 +2017-06-08,0.00239181338976735 +2017-06-09,-0.03647602336042388 +2017-06-10,0.0 +2017-06-11,0.0 +2017-06-12,-0.019600670756011573 +2017-06-13,0.011719996633973828 +2017-06-14,-0.004517691146136205 +2017-06-15,-0.006462744925185726 +2017-06-16,0.003649830095089568 +2017-06-17,0.0 +2017-06-18,0.0 +2017-06-19,0.01474666392254731 +2017-06-20,-0.006070728522620472 +2017-06-21,0.011208703959754596 +2017-06-22,-0.0017299594296935616 +2017-06-23,0.009586664357720265 +2017-06-24,0.0 +2017-06-25,0.0 +2017-06-26,-0.00791035489361247 +2017-06-27,-0.023316985476341978 +2017-06-28,0.015058079881250297 +2017-06-29,-0.017436066678588102 +2017-06-30,-0.003885212744659694 +2017-07-01,0.0 +2017-07-02,0.0 +2017-07-03,-0.013723681554019707 +2017-07-04,0.0 +2017-07-05,0.011924345490909483 +2017-07-06,-0.007856270148961508 +2017-07-07,0.01665639487976467 +2017-07-08,0.0 +2017-07-09,0.0 +2017-07-10,0.01270344209761931 +2017-07-11,0.005095059453382955 +2017-07-12,0.015823497855613766 +2017-07-13,0.0013264449122236677 +2017-07-14,0.008285122367519993 +2017-07-15,0.0 +2017-07-16,0.0 +2017-07-17,0.002520250238294164 +2017-07-18,0.03646227724287132 +2017-07-19,0.004568602880205885 +2017-07-20,-0.00045973516866448794 +2017-07-21,0.0049118814817794565 +2017-07-22,0.0 +2017-07-23,0.0 +2017-07-24,0.006982952864066805 +2017-07-25,-0.006477181272521837 +2017-07-26,0.005253415236657233 +2017-07-27,-0.008517760122170537 +2017-07-28,-0.0011895227266641717 +2017-07-29,0.0 +2017-07-30,0.0 +2017-07-31,-0.016076010474634805 +2017-08-01,0.004605653993462244 +2017-08-02,0.007429078037390235 +2017-08-03,-0.007736774885445219 +2017-08-04,0.004711013210118242 +2017-08-05,0.0 +2017-08-06,0.0 +2017-08-07,0.003923088780792616 +2017-08-08,0.000648545075997653 +2017-08-09,-0.004093525962214795 +2017-08-10,-0.027789514245592062 +2017-08-11,0.010063720711702473 +2017-08-12,0.0 +2017-08-13,0.0 +2017-08-14,0.010553782348560858 +2017-08-15,-0.0006964212280783366 +2017-08-16,0.00014039931479548073 +2017-08-17,-0.01889010458067142 +2017-08-18,-6.087848366631565e-05 +2017-08-19,0.0 +2017-08-20,0.0 +2017-08-21,-0.0018554297739434844 +2017-08-22,0.015533317270306934 +2017-08-23,-0.002497102934827855 +2017-08-24,-0.005445441760645209 +2017-08-25,-0.006359319759217663 +2017-08-26,0.0 +2017-08-27,0.0 +2017-08-28,0.004184983139484011 +2017-08-29,0.008022154024211137 +2017-08-30,0.014077349305761988 +2017-08-31,0.00824387901325907 +2017-09-01,-0.0010133676620260879 +2017-09-02,0.0 +2017-09-03,0.0 +2017-09-04,0.0 +2017-09-05,-0.008963923599061396 +2017-09-06,0.007488780562387554 +2017-09-07,0.0042289046498253935 +2017-09-08,-0.013173317580561061 +2017-09-09,0.0 +2017-09-10,0.0 +2017-09-11,0.015416394784739662 +2017-09-12,0.0038739180064544243 +2017-09-13,0.0012254744196069645 +2017-09-14,-0.008815268141659782 +2017-09-15,3.8948642555380886e-05 +2017-09-16,0.0 +2017-09-17,0.0 +2017-09-18,-0.004283069052282373 +2017-09-19,0.004789691135404839 +2017-09-20,-0.001012202899489667 +2017-09-21,-0.0028820721558969753 +2017-09-22,-0.006936574508851656 +2017-09-23,0.0 +2017-09-24,0.0 +2017-09-25,-0.025138000071466382 +2017-09-26,0.006392085530634972 +2017-09-27,0.015682140033445258 +2017-09-28,0.0007953566555651825 +2017-09-29,0.0071110130077043365 +2017-09-30,0.0 +2017-10-01,0.0 +2017-10-02,-0.008430711859048534 +2017-10-03,0.004455771989419155 +2017-10-04,0.003309684397314827 +2017-10-05,0.023663578955761322 +2017-10-06,0.008274409857515375 +2017-10-07,0.0 +2017-10-08,0.0 +2017-10-09,-0.00021135549864259584 +2017-10-10,-0.0043992309324013545 +2017-10-11,0.007123303261804992 +2017-10-12,0.001166357726604783 +2017-10-13,0.007154400232885383 +2017-10-14,0.0 +2017-10-15,0.0 +2017-10-16,0.008712838950892143 +2017-10-17,0.0001998199618145069 +2017-10-18,-0.00692626342960867 +2017-10-19,-0.011045481081886965 +2017-10-20,-0.00039198100579318496 +2017-10-21,0.0 +2017-10-22,0.0 +2017-10-23,-0.013165531062315772 +2017-10-24,0.008061850730660902 +2017-10-25,-0.004551487053533809 +2017-10-26,0.002707193208362213 +2017-10-27,0.054701937184086 +2017-10-28,0.0 +2017-10-29,0.0 +2017-10-30,0.007033776148992921 +2017-10-31,8.605302226016853e-05 +2017-11-01,0.00334683343811637 +2017-11-02,-0.0029320203998577377 +2017-11-03,0.010357383798537567 +2017-11-04,0.0 +2017-11-05,0.0 +2017-11-06,0.0037731852107398822 +2017-11-07,-0.0011725472204969778 +2017-11-08,0.0 +2017-11-09,-0.0016528286856620777 +2017-11-10,-0.005603005658312399 +2017-11-11,0.0 +2017-11-12,0.0 +2017-11-13,0.0027666555440064577 +2017-11-14,-0.001748654528646032 +2017-11-15,-0.00915940460366782 +2017-11-16,0.011928092308623572 +2017-11-17,-0.007669370269785314 +2017-11-18,0.0 +2017-11-19,0.0 +2017-11-20,-0.00041279810230709924 +2017-11-21,0.014735659451300842 +2017-11-22,0.004229667602330345 +2017-11-23,0.0 +2017-11-24,0.00745321906196561 +2017-11-25,0.0 +2017-11-26,0.0 +2017-11-27,0.0030477701872400065 +2017-11-28,0.00039797953887465987 +2017-11-29,-0.03315426280662692 +2017-11-30,0.006849587471798202 +2017-12-01,-0.008602058756149389 +2017-12-02,0.0 +2017-12-03,0.0 +2017-12-04,-0.01603235682020343 +2017-12-05,0.00441310767542924 +2017-12-06,0.00846484496647859 +2017-12-07,0.008553857381969898 +2017-12-08,0.003445120079279551 +2017-12-09,0.0 +2017-12-10,0.0 +2017-12-11,0.0031099963914891227 +2017-12-12,-0.005180287892549984 +2017-12-13,0.004793630437407437 +2017-12-14,0.00464480954910762 +2017-12-15,0.007955703343966114 +2017-12-16,0.0 +2017-12-17,0.0 +2017-12-18,0.008184138854549161 +2017-12-19,-0.008540953901812864 +2017-12-20,-0.0028387235954262513 +2017-12-21,-0.0009082045171374675 +2017-12-22,-0.00030366484842214714 +2017-12-23,0.0 +2017-12-24,0.0 +2017-12-25,0.0 +2017-12-26,-0.007886401177494395 +2017-12-27,0.00011385599674484936 +2017-12-28,0.007692965452065881 +2017-12-29,-0.007755641308840877 diff --git a/alphapy/examples/Trading System/systems/faang_closer_trades_1d.csv b/alphapy/examples/Trading System/systems/faang_closer_trades_1d.csv new file mode 100644 index 0000000..d96ca00 --- /dev/null +++ b/alphapy/examples/Trading System/systems/faang_closer_trades_1d.csv @@ -0,0 +1,1748 @@ +date,name,order,quantity,price +2016-08-19,nflx,se,-1,95.87 +2016-08-19,amzn,se,-1,757.31 +2016-08-19,fb,se,-1,123.56 +2016-08-19,aapl,le,1,109.36 +2016-08-19,googl,se,-1,799.65 +2016-08-22,amzn,sx,1,759.48 +2016-08-22,amzn,le,1,759.48 +2016-08-22,fb,sx,1,124.15 +2016-08-22,fb,le,1,124.15 +2016-08-22,aapl,lx,-1,108.51 +2016-08-22,aapl,se,-1,108.51 +2016-08-23,nflx,sx,1,95.94 +2016-08-23,nflx,le,1,95.94 +2016-08-23,aapl,sx,1,108.85 +2016-08-23,aapl,le,1,108.85 +2016-08-24,nflx,lx,-1,95.18 +2016-08-24,nflx,se,-1,95.18 +2016-08-24,amzn,lx,-1,757.25 +2016-08-24,amzn,se,-1,757.25 +2016-08-24,fb,lx,-1,123.48 +2016-08-24,fb,se,-1,123.48 +2016-08-24,aapl,lx,-1,108.03 +2016-08-24,aapl,se,-1,108.03 +2016-08-25,nflx,sx,1,97.32 +2016-08-25,nflx,le,1,97.32 +2016-08-25,amzn,sx,1,759.22 +2016-08-25,amzn,le,1,759.22 +2016-08-25,fb,sx,1,123.89 +2016-08-25,fb,le,1,123.89 +2016-08-26,googl,sx,1,793.22 +2016-08-26,googl,le,1,793.22 +2016-08-29,nflx,lx,-1,97.3 +2016-08-29,nflx,se,-1,97.3 +2016-08-30,nflx,sx,1,97.45 +2016-08-30,nflx,le,1,97.45 +2016-08-30,amzn,lx,-1,767.58 +2016-08-30,amzn,se,-1,767.58 +2016-08-30,fb,lx,-1,125.84 +2016-08-30,fb,se,-1,125.84 +2016-08-30,googl,lx,-1,791.92 +2016-08-30,googl,se,-1,791.92 +2016-08-31,amzn,sx,1,769.16 +2016-08-31,amzn,le,1,769.16 +2016-08-31,fb,sx,1,126.12 +2016-08-31,fb,le,1,126.12 +2016-08-31,aapl,sx,1,106.1 +2016-08-31,aapl,le,1,106.1 +2016-09-01,nflx,lx,-1,97.38 +2016-09-01,nflx,se,-1,97.38 +2016-09-01,googl,sx,1,791.4 +2016-09-01,googl,le,1,791.4 +2016-09-06,nflx,sx,1,100.09 +2016-09-06,nflx,le,1,100.09 +2016-09-06,aapl,lx,-1,107.7 +2016-09-06,aapl,se,-1,107.7 +2016-09-07,nflx,lx,-1,99.15 +2016-09-07,nflx,se,-1,99.15 +2016-09-07,amzn,lx,-1,784.48 +2016-09-07,amzn,se,-1,784.48 +2016-09-07,aapl,sx,1,108.36 +2016-09-07,aapl,le,1,108.36 +2016-09-07,googl,lx,-1,807.99 +2016-09-07,googl,se,-1,807.99 +2016-09-08,nflx,sx,1,99.66 +2016-09-08,nflx,le,1,99.66 +2016-09-08,fb,lx,-1,130.27 +2016-09-08,fb,se,-1,130.27 +2016-09-08,aapl,lx,-1,105.52 +2016-09-08,aapl,se,-1,105.52 +2016-09-09,nflx,lx,-1,96.5 +2016-09-09,nflx,se,-1,96.5 +2016-09-12,nflx,sx,1,99.05 +2016-09-12,nflx,le,1,99.05 +2016-09-12,amzn,sx,1,771.49 +2016-09-12,amzn,le,1,771.49 +2016-09-12,fb,sx,1,128.69 +2016-09-12,fb,le,1,128.69 +2016-09-12,aapl,sx,1,105.44 +2016-09-12,aapl,le,1,105.44 +2016-09-12,googl,sx,1,798.82 +2016-09-12,googl,le,1,798.82 +2016-09-13,nflx,lx,-1,96.09 +2016-09-13,nflx,se,-1,96.09 +2016-09-13,amzn,lx,-1,761.01 +2016-09-13,amzn,se,-1,761.01 +2016-09-13,fb,lx,-1,127.21 +2016-09-13,fb,se,-1,127.21 +2016-09-13,googl,lx,-1,788.72 +2016-09-13,googl,se,-1,788.72 +2016-09-14,nflx,sx,1,97.01 +2016-09-14,nflx,le,1,97.01 +2016-09-14,amzn,sx,1,761.09 +2016-09-14,amzn,le,1,761.09 +2016-09-14,fb,sx,1,127.77 +2016-09-14,fb,le,1,127.77 +2016-09-14,googl,sx,1,790.46 +2016-09-14,googl,le,1,790.46 +2016-09-16,aapl,lx,-1,114.92 +2016-09-16,aapl,se,-1,114.92 +2016-09-16,googl,lx,-1,797.97 +2016-09-16,googl,se,-1,797.97 +2016-09-19,nflx,lx,-1,98.06 +2016-09-19,nflx,se,-1,98.06 +2016-09-19,amzn,lx,-1,775.1 +2016-09-19,amzn,se,-1,775.1 +2016-09-19,fb,lx,-1,128.65 +2016-09-19,fb,se,-1,128.65 +2016-09-20,nflx,sx,1,98.25 +2016-09-20,nflx,le,1,98.25 +2016-09-20,amzn,sx,1,780.22 +2016-09-20,amzn,le,1,780.22 +2016-09-20,googl,sx,1,799.78 +2016-09-20,googl,le,1,799.78 +2016-09-21,nflx,lx,-1,94.88 +2016-09-21,nflx,se,-1,94.88 +2016-09-21,fb,sx,1,129.94 +2016-09-21,fb,le,1,129.94 +2016-09-22,nflx,sx,1,95.83 +2016-09-22,nflx,le,1,95.83 +2016-09-22,aapl,sx,1,114.62 +2016-09-22,aapl,le,1,114.62 +2016-09-23,fb,lx,-1,127.96 +2016-09-23,fb,se,-1,127.96 +2016-09-23,aapl,lx,-1,112.71 +2016-09-23,aapl,se,-1,112.71 +2016-09-23,googl,lx,-1,814.96 +2016-09-23,googl,se,-1,814.96 +2016-09-26,nflx,lx,-1,94.56 +2016-09-26,nflx,se,-1,94.56 +2016-09-26,amzn,lx,-1,799.16 +2016-09-26,amzn,se,-1,799.16 +2016-09-26,aapl,sx,1,112.88 +2016-09-26,aapl,le,1,112.88 +2016-09-27,nflx,sx,1,97.07 +2016-09-27,nflx,le,1,97.07 +2016-09-27,amzn,sx,1,816.11 +2016-09-27,amzn,le,1,816.11 +2016-09-27,fb,sx,1,128.69 +2016-09-27,fb,le,1,128.69 +2016-09-27,googl,sx,1,810.73 +2016-09-27,googl,le,1,810.73 +2016-09-28,googl,lx,-1,810.06 +2016-09-28,googl,se,-1,810.06 +2016-09-29,nflx,lx,-1,96.67 +2016-09-29,nflx,se,-1,96.67 +2016-09-29,fb,lx,-1,128.09 +2016-09-29,fb,se,-1,128.09 +2016-09-29,aapl,lx,-1,112.18 +2016-09-29,aapl,se,-1,112.18 +2016-09-30,nflx,sx,1,98.55 +2016-09-30,nflx,le,1,98.55 +2016-09-30,fb,sx,1,128.27 +2016-09-30,fb,le,1,128.27 +2016-09-30,aapl,sx,1,113.05 +2016-09-30,aapl,le,1,113.05 +2016-09-30,googl,sx,1,804.06 +2016-09-30,googl,le,1,804.06 +2016-10-03,amzn,lx,-1,836.74 +2016-10-03,amzn,se,-1,836.74 +2016-10-03,aapl,lx,-1,112.52 +2016-10-03,aapl,se,-1,112.52 +2016-10-03,googl,lx,-1,800.38 +2016-10-03,googl,se,-1,800.38 +2016-10-04,nflx,lx,-1,102.34 +2016-10-04,nflx,se,-1,102.34 +2016-10-04,fb,lx,-1,128.19 +2016-10-04,fb,se,-1,128.19 +2016-10-04,aapl,sx,1,113.0 +2016-10-04,aapl,le,1,113.0 +2016-10-04,googl,sx,1,802.79 +2016-10-04,googl,le,1,802.79 +2016-10-05,nflx,sx,1,106.28 +2016-10-05,nflx,le,1,106.28 +2016-10-05,amzn,sx,1,844.36 +2016-10-05,amzn,le,1,844.36 +2016-10-05,fb,sx,1,128.47 +2016-10-05,fb,le,1,128.47 +2016-10-05,googl,lx,-1,801.23 +2016-10-05,googl,se,-1,801.23 +2016-10-06,nflx,lx,-1,105.07 +2016-10-06,nflx,se,-1,105.07 +2016-10-06,amzn,lx,-1,841.66 +2016-10-06,amzn,se,-1,841.66 +2016-10-06,googl,sx,1,803.08 +2016-10-06,googl,le,1,803.08 +2016-10-07,googl,lx,-1,800.71 +2016-10-07,googl,se,-1,800.71 +2016-10-10,amzn,sx,1,841.71 +2016-10-10,amzn,le,1,841.71 +2016-10-10,googl,sx,1,814.17 +2016-10-10,googl,le,1,814.17 +2016-10-11,amzn,lx,-1,831.0 +2016-10-11,amzn,se,-1,831.0 +2016-10-11,fb,lx,-1,128.88 +2016-10-11,fb,se,-1,128.88 +2016-10-11,googl,lx,-1,809.57 +2016-10-11,googl,se,-1,809.57 +2016-10-12,amzn,sx,1,834.09 +2016-10-12,amzn,le,1,834.09 +2016-10-12,fb,sx,1,129.05 +2016-10-12,fb,le,1,129.05 +2016-10-12,googl,sx,1,811.77 +2016-10-12,googl,le,1,811.77 +2016-10-13,nflx,sx,1,100.23 +2016-10-13,nflx,le,1,100.23 +2016-10-13,amzn,lx,-1,829.28 +2016-10-13,amzn,se,-1,829.28 +2016-10-13,fb,lx,-1,127.82 +2016-10-13,fb,se,-1,127.82 +2016-10-13,aapl,lx,-1,116.98 +2016-10-13,aapl,se,-1,116.98 +2016-10-13,googl,lx,-1,804.08 +2016-10-13,googl,se,-1,804.08 +2016-10-14,fb,sx,1,127.88 +2016-10-14,fb,le,1,127.88 +2016-10-14,aapl,sx,1,117.63 +2016-10-14,aapl,le,1,117.63 +2016-10-14,googl,sx,1,804.6 +2016-10-14,googl,le,1,804.6 +2016-10-17,nflx,lx,-1,99.8 +2016-10-17,nflx,se,-1,99.8 +2016-10-17,fb,lx,-1,127.54 +2016-10-17,fb,se,-1,127.54 +2016-10-17,aapl,lx,-1,117.55 +2016-10-17,aapl,se,-1,117.55 +2016-10-18,nflx,sx,1,118.79 +2016-10-18,nflx,le,1,118.79 +2016-10-18,amzn,sx,1,817.65 +2016-10-18,amzn,le,1,817.65 +2016-10-18,fb,sx,1,128.57 +2016-10-18,fb,le,1,128.57 +2016-10-20,amzn,lx,-1,810.32 +2016-10-20,amzn,se,-1,810.32 +2016-10-20,fb,lx,-1,130.0 +2016-10-20,fb,se,-1,130.0 +2016-10-20,googl,lx,-1,821.63 +2016-10-20,googl,se,-1,821.63 +2016-10-21,amzn,sx,1,818.99 +2016-10-21,amzn,le,1,818.99 +2016-10-21,fb,sx,1,132.07 +2016-10-21,fb,le,1,132.07 +2016-10-21,googl,sx,1,824.06 +2016-10-21,googl,le,1,824.06 +2016-10-24,nflx,lx,-1,127.33 +2016-10-24,nflx,se,-1,127.33 +2016-10-24,aapl,sx,1,117.65 +2016-10-24,aapl,le,1,117.65 +2016-10-25,amzn,lx,-1,835.18 +2016-10-25,amzn,se,-1,835.18 +2016-10-25,fb,lx,-1,132.29 +2016-10-25,fb,se,-1,132.29 +2016-10-25,googl,lx,-1,828.55 +2016-10-25,googl,se,-1,828.55 +2016-10-26,nflx,sx,1,126.97 +2016-10-26,nflx,le,1,126.97 +2016-10-26,aapl,lx,-1,115.59 +2016-10-26,aapl,se,-1,115.59 +2016-10-27,nflx,lx,-1,126.47 +2016-10-27,nflx,se,-1,126.47 +2016-10-28,nflx,sx,1,126.57 +2016-10-28,nflx,le,1,126.57 +2016-10-28,fb,sx,1,131.29 +2016-10-28,fb,le,1,131.29 +2016-10-28,googl,sx,1,819.56 +2016-10-28,googl,le,1,819.56 +2016-10-31,nflx,lx,-1,124.87 +2016-10-31,nflx,se,-1,124.87 +2016-10-31,amzn,sx,1,789.82 +2016-10-31,amzn,le,1,789.82 +2016-10-31,fb,lx,-1,130.99 +2016-10-31,fb,se,-1,130.99 +2016-10-31,googl,lx,-1,809.9 +2016-10-31,googl,se,-1,809.9 +2016-11-01,amzn,lx,-1,785.41 +2016-11-01,amzn,se,-1,785.41 +2016-11-02,aapl,sx,1,111.59 +2016-11-02,aapl,le,1,111.59 +2016-11-03,amzn,sx,1,767.03 +2016-11-03,amzn,le,1,767.03 +2016-11-03,aapl,lx,-1,109.83 +2016-11-03,aapl,se,-1,109.83 +2016-11-04,amzn,lx,-1,755.05 +2016-11-04,amzn,se,-1,755.05 +2016-11-04,fb,sx,1,120.75 +2016-11-04,fb,le,1,120.75 +2016-11-07,nflx,sx,1,124.58 +2016-11-07,nflx,le,1,124.58 +2016-11-07,amzn,sx,1,784.93 +2016-11-07,amzn,le,1,784.93 +2016-11-07,aapl,sx,1,110.41 +2016-11-07,aapl,le,1,110.41 +2016-11-07,googl,sx,1,802.03 +2016-11-07,googl,le,1,802.03 +2016-11-08,nflx,lx,-1,124.34 +2016-11-08,nflx,se,-1,124.34 +2016-11-09,amzn,lx,-1,771.88 +2016-11-09,amzn,se,-1,771.88 +2016-11-09,fb,lx,-1,123.18 +2016-11-09,fb,se,-1,123.18 +2016-11-09,aapl,lx,-1,110.88 +2016-11-09,aapl,se,-1,110.88 +2016-11-09,googl,lx,-1,805.59 +2016-11-09,googl,se,-1,805.59 +2016-11-11,aapl,sx,1,108.43 +2016-11-11,aapl,le,1,108.43 +2016-11-14,aapl,lx,-1,105.71 +2016-11-14,aapl,se,-1,105.71 +2016-11-15,nflx,sx,1,113.59 +2016-11-15,nflx,le,1,113.59 +2016-11-15,amzn,sx,1,743.24 +2016-11-15,amzn,le,1,743.24 +2016-11-15,fb,sx,1,117.2 +2016-11-15,fb,le,1,117.2 +2016-11-15,aapl,sx,1,107.11 +2016-11-15,aapl,le,1,107.11 +2016-11-15,googl,sx,1,775.16 +2016-11-15,googl,le,1,775.16 +2016-11-16,fb,lx,-1,116.34 +2016-11-16,fb,se,-1,116.34 +2016-11-17,nflx,lx,-1,115.03 +2016-11-17,nflx,se,-1,115.03 +2016-11-17,fb,sx,1,117.79 +2016-11-17,fb,le,1,117.79 +2016-11-17,aapl,lx,-1,109.95 +2016-11-17,aapl,se,-1,109.95 +2016-11-18,nflx,sx,1,115.21 +2016-11-18,nflx,le,1,115.21 +2016-11-18,fb,lx,-1,117.02 +2016-11-18,fb,se,-1,117.02 +2016-11-18,aapl,sx,1,110.06 +2016-11-18,aapl,le,1,110.06 +2016-11-18,googl,lx,-1,775.97 +2016-11-18,googl,se,-1,775.97 +2016-11-21,fb,sx,1,121.77 +2016-11-21,fb,le,1,121.77 +2016-11-21,googl,sx,1,784.8 +2016-11-21,googl,le,1,784.8 +2016-11-22,fb,lx,-1,121.47 +2016-11-22,fb,se,-1,121.47 +2016-11-23,nflx,lx,-1,117.69 +2016-11-23,nflx,se,-1,117.69 +2016-11-23,amzn,lx,-1,780.12 +2016-11-23,amzn,se,-1,780.12 +2016-11-23,aapl,lx,-1,111.23 +2016-11-23,aapl,se,-1,111.23 +2016-11-23,googl,lx,-1,779.0 +2016-11-23,googl,se,-1,779.0 +2016-11-25,amzn,sx,1,780.37 +2016-11-25,amzn,le,1,780.37 +2016-11-25,aapl,sx,1,111.79 +2016-11-25,aapl,le,1,111.79 +2016-11-25,googl,sx,1,780.23 +2016-11-25,googl,le,1,780.23 +2016-11-28,amzn,lx,-1,766.77 +2016-11-28,amzn,se,-1,766.77 +2016-11-28,fb,sx,1,120.41 +2016-11-28,fb,le,1,120.41 +2016-11-28,aapl,lx,-1,111.57 +2016-11-28,aapl,se,-1,111.57 +2016-11-29,nflx,sx,1,117.51 +2016-11-29,nflx,le,1,117.51 +2016-11-30,nflx,lx,-1,117.0 +2016-11-30,nflx,se,-1,117.0 +2016-11-30,fb,lx,-1,118.42 +2016-11-30,fb,se,-1,118.42 +2016-11-30,googl,lx,-1,775.88 +2016-11-30,googl,se,-1,775.88 +2016-12-01,nflx,sx,1,117.22 +2016-12-01,nflx,le,1,117.22 +2016-12-02,fb,sx,1,115.4 +2016-12-02,fb,le,1,115.4 +2016-12-02,aapl,sx,1,109.9 +2016-12-02,aapl,le,1,109.9 +2016-12-02,googl,sx,1,764.46 +2016-12-02,googl,le,1,764.46 +2016-12-05,nflx,lx,-1,119.16 +2016-12-05,nflx,se,-1,119.16 +2016-12-05,amzn,sx,1,759.36 +2016-12-05,amzn,le,1,759.36 +2016-12-05,aapl,lx,-1,109.11 +2016-12-05,aapl,se,-1,109.11 +2016-12-06,nflx,sx,1,124.57 +2016-12-06,nflx,le,1,124.57 +2016-12-06,fb,lx,-1,117.31 +2016-12-06,fb,se,-1,117.31 +2016-12-06,aapl,sx,1,109.95 +2016-12-06,aapl,le,1,109.95 +2016-12-06,googl,lx,-1,776.18 +2016-12-06,googl,se,-1,776.18 +2016-12-07,fb,sx,1,117.95 +2016-12-07,fb,le,1,117.95 +2016-12-07,googl,sx,1,791.47 +2016-12-07,googl,le,1,791.47 +2016-12-08,nflx,lx,-1,123.24 +2016-12-08,nflx,se,-1,123.24 +2016-12-08,amzn,lx,-1,767.33 +2016-12-08,amzn,se,-1,767.33 +2016-12-09,amzn,sx,1,768.66 +2016-12-09,amzn,le,1,768.66 +2016-12-12,amzn,lx,-1,760.12 +2016-12-12,amzn,se,-1,760.12 +2016-12-12,fb,lx,-1,117.77 +2016-12-12,fb,se,-1,117.77 +2016-12-12,aapl,lx,-1,113.3 +2016-12-12,aapl,se,-1,113.3 +2016-12-12,googl,lx,-1,807.9 +2016-12-12,googl,se,-1,807.9 +2016-12-13,nflx,sx,1,123.78 +2016-12-13,nflx,le,1,123.78 +2016-12-13,amzn,sx,1,774.34 +2016-12-13,amzn,le,1,774.34 +2016-12-13,fb,sx,1,120.31 +2016-12-13,fb,le,1,120.31 +2016-12-13,aapl,sx,1,115.19 +2016-12-13,aapl,le,1,115.19 +2016-12-13,googl,sx,1,815.34 +2016-12-13,googl,le,1,815.34 +2016-12-14,nflx,lx,-1,123.44 +2016-12-14,nflx,se,-1,123.44 +2016-12-14,amzn,lx,-1,768.82 +2016-12-14,amzn,se,-1,768.82 +2016-12-14,fb,lx,-1,120.21 +2016-12-14,fb,se,-1,120.21 +2016-12-15,nflx,sx,1,125.0 +2016-12-15,nflx,le,1,125.0 +2016-12-15,fb,sx,1,120.57 +2016-12-15,fb,le,1,120.57 +2016-12-15,googl,lx,-1,815.65 +2016-12-15,googl,se,-1,815.65 +2016-12-16,nflx,lx,-1,124.22 +2016-12-16,nflx,se,-1,124.22 +2016-12-16,fb,lx,-1,119.87 +2016-12-16,fb,se,-1,119.87 +2016-12-19,nflx,sx,1,125.45 +2016-12-19,nflx,le,1,125.45 +2016-12-19,amzn,sx,1,766.0 +2016-12-19,amzn,le,1,766.0 +2016-12-19,googl,sx,1,812.5 +2016-12-19,googl,le,1,812.5 +2016-12-20,nflx,lx,-1,125.12 +2016-12-20,nflx,se,-1,125.12 +2016-12-21,nflx,sx,1,126.5 +2016-12-21,nflx,le,1,126.5 +2016-12-21,amzn,lx,-1,770.6 +2016-12-21,amzn,se,-1,770.6 +2016-12-21,googl,lx,-1,812.2 +2016-12-21,googl,se,-1,812.2 +2016-12-22,nflx,lx,-1,125.58 +2016-12-22,nflx,se,-1,125.58 +2016-12-22,aapl,lx,-1,116.29 +2016-12-22,aapl,se,-1,116.29 +2016-12-23,nflx,sx,1,125.59 +2016-12-23,nflx,le,1,125.59 +2016-12-23,aapl,sx,1,116.52 +2016-12-23,aapl,le,1,116.52 +2016-12-27,amzn,sx,1,771.4 +2016-12-27,amzn,le,1,771.4 +2016-12-27,fb,sx,1,118.01 +2016-12-27,fb,le,1,118.01 +2016-12-27,googl,sx,1,809.93 +2016-12-27,googl,le,1,809.93 +2016-12-28,nflx,lx,-1,125.89 +2016-12-28,nflx,se,-1,125.89 +2016-12-28,fb,lx,-1,116.92 +2016-12-28,fb,se,-1,116.92 +2016-12-28,aapl,lx,-1,116.76 +2016-12-28,aapl,se,-1,116.76 +2016-12-28,googl,lx,-1,804.57 +2016-12-28,googl,se,-1,804.57 +2016-12-29,amzn,lx,-1,765.15 +2016-12-29,amzn,se,-1,765.15 +2017-01-03,nflx,sx,1,127.49 +2017-01-03,nflx,le,1,127.49 +2017-01-03,amzn,sx,1,753.67 +2017-01-03,amzn,le,1,753.67 +2017-01-03,fb,sx,1,116.86 +2017-01-03,fb,le,1,116.86 +2017-01-03,aapl,sx,1,116.15 +2017-01-03,aapl,le,1,116.15 +2017-01-03,googl,sx,1,808.01 +2017-01-03,googl,le,1,808.01 +2017-01-04,aapl,lx,-1,116.02 +2017-01-04,aapl,se,-1,116.02 +2017-01-04,googl,lx,-1,807.77 +2017-01-04,googl,se,-1,807.77 +2017-01-05,aapl,sx,1,116.61 +2017-01-05,aapl,le,1,116.61 +2017-01-05,googl,sx,1,813.02 +2017-01-05,googl,le,1,813.02 +2017-01-06,nflx,lx,-1,131.07 +2017-01-06,nflx,se,-1,131.07 +2017-01-10,amzn,lx,-1,795.9 +2017-01-10,amzn,se,-1,795.9 +2017-01-10,fb,lx,-1,124.35 +2017-01-10,fb,se,-1,124.35 +2017-01-10,googl,lx,-1,826.01 +2017-01-10,googl,se,-1,826.01 +2017-01-11,nflx,sx,1,130.5 +2017-01-11,nflx,le,1,130.5 +2017-01-11,amzn,sx,1,799.02 +2017-01-11,amzn,le,1,799.02 +2017-01-11,fb,sx,1,126.09 +2017-01-11,fb,le,1,126.09 +2017-01-11,googl,sx,1,829.86 +2017-01-11,googl,le,1,829.86 +2017-01-12,nflx,lx,-1,129.18 +2017-01-12,nflx,se,-1,129.18 +2017-01-12,aapl,lx,-1,119.25 +2017-01-12,aapl,se,-1,119.25 +2017-01-12,googl,lx,-1,829.53 +2017-01-12,googl,se,-1,829.53 +2017-01-13,nflx,sx,1,133.7 +2017-01-13,nflx,le,1,133.7 +2017-01-13,googl,sx,1,830.94 +2017-01-13,googl,le,1,830.94 +2017-01-17,nflx,lx,-1,132.89 +2017-01-17,nflx,se,-1,132.89 +2017-01-17,amzn,lx,-1,809.72 +2017-01-17,amzn,se,-1,809.72 +2017-01-17,fb,lx,-1,127.87 +2017-01-17,fb,se,-1,127.87 +2017-01-17,aapl,sx,1,120.0 +2017-01-17,aapl,le,1,120.0 +2017-01-17,googl,lx,-1,827.46 +2017-01-17,googl,se,-1,827.46 +2017-01-18,nflx,sx,1,133.26 +2017-01-18,nflx,le,1,133.26 +2017-01-18,fb,sx,1,127.92 +2017-01-18,fb,le,1,127.92 +2017-01-18,aapl,lx,-1,119.99 +2017-01-18,aapl,se,-1,119.99 +2017-01-18,googl,sx,1,829.02 +2017-01-18,googl,le,1,829.02 +2017-01-19,amzn,sx,1,809.04 +2017-01-19,amzn,le,1,809.04 +2017-01-19,fb,lx,-1,127.55 +2017-01-19,fb,se,-1,127.55 +2017-01-19,googl,lx,-1,824.37 +2017-01-19,googl,se,-1,824.37 +2017-01-20,amzn,lx,-1,808.33 +2017-01-20,amzn,se,-1,808.33 +2017-01-20,aapl,sx,1,120.0 +2017-01-20,aapl,le,1,120.0 +2017-01-20,googl,sx,1,828.17 +2017-01-20,googl,le,1,828.17 +2017-01-23,nflx,lx,-1,137.39 +2017-01-23,nflx,se,-1,137.39 +2017-01-23,amzn,sx,1,817.88 +2017-01-23,amzn,le,1,817.88 +2017-01-23,fb,sx,1,128.93 +2017-01-23,fb,le,1,128.93 +2017-01-24,nflx,sx,1,140.11 +2017-01-24,nflx,le,1,140.11 +2017-01-24,aapl,lx,-1,119.97 +2017-01-24,aapl,se,-1,119.97 +2017-01-25,nflx,lx,-1,139.52 +2017-01-25,nflx,se,-1,139.52 +2017-01-25,aapl,sx,1,121.88 +2017-01-25,aapl,le,1,121.88 +2017-01-26,googl,lx,-1,856.98 +2017-01-26,googl,se,-1,856.98 +2017-01-27,nflx,sx,1,142.45 +2017-01-27,nflx,le,1,142.45 +2017-01-27,amzn,lx,-1,835.77 +2017-01-27,amzn,se,-1,835.77 +2017-01-27,fb,lx,-1,132.18 +2017-01-27,fb,se,-1,132.18 +2017-01-30,nflx,lx,-1,141.22 +2017-01-30,nflx,se,-1,141.22 +2017-01-30,aapl,lx,-1,121.63 +2017-01-30,aapl,se,-1,121.63 +2017-02-01,nflx,sx,1,140.78 +2017-02-01,nflx,le,1,140.78 +2017-02-01,amzn,sx,1,832.35 +2017-02-01,amzn,le,1,832.35 +2017-02-01,fb,sx,1,133.23 +2017-02-01,fb,le,1,133.23 +2017-02-01,aapl,sx,1,128.75 +2017-02-01,aapl,le,1,128.75 +2017-02-02,nflx,lx,-1,139.2 +2017-02-02,nflx,se,-1,139.2 +2017-02-02,fb,lx,-1,130.84 +2017-02-02,fb,se,-1,130.84 +2017-02-02,aapl,lx,-1,128.53 +2017-02-02,aapl,se,-1,128.53 +2017-02-02,googl,sx,1,818.26 +2017-02-02,googl,le,1,818.26 +2017-02-03,nflx,sx,1,140.25 +2017-02-03,nflx,le,1,140.25 +2017-02-03,amzn,lx,-1,810.2 +2017-02-03,amzn,se,-1,810.2 +2017-02-03,fb,sx,1,130.98 +2017-02-03,fb,le,1,130.98 +2017-02-03,aapl,sx,1,129.08 +2017-02-03,aapl,le,1,129.08 +2017-02-07,amzn,sx,1,812.5 +2017-02-07,amzn,le,1,812.5 +2017-02-07,fb,lx,-1,131.84 +2017-02-07,fb,se,-1,131.84 +2017-02-08,fb,sx,1,134.2 +2017-02-08,fb,le,1,134.2 +2017-02-09,nflx,lx,-1,144.14 +2017-02-09,nflx,se,-1,144.14 +2017-02-09,fb,lx,-1,134.14 +2017-02-09,fb,se,-1,134.14 +2017-02-10,nflx,sx,1,144.82 +2017-02-10,nflx,le,1,144.82 +2017-02-10,fb,sx,1,134.19 +2017-02-10,fb,le,1,134.19 +2017-02-10,aapl,lx,-1,132.12 +2017-02-10,aapl,se,-1,132.12 +2017-02-13,nflx,lx,-1,143.2 +2017-02-13,nflx,se,-1,143.2 +2017-02-13,fb,lx,-1,134.05 +2017-02-13,fb,se,-1,134.05 +2017-02-13,aapl,sx,1,133.29 +2017-02-13,aapl,le,1,133.29 +2017-02-14,amzn,lx,-1,836.39 +2017-02-14,amzn,se,-1,836.39 +2017-02-15,nflx,sx,1,142.27 +2017-02-15,nflx,le,1,142.27 +2017-02-15,amzn,sx,1,842.7 +2017-02-15,amzn,le,1,842.7 +2017-02-15,googl,lx,-1,837.32 +2017-02-15,googl,se,-1,837.32 +2017-02-16,nflx,lx,-1,142.01 +2017-02-16,nflx,se,-1,142.01 +2017-02-16,fb,sx,1,133.84 +2017-02-16,fb,le,1,133.84 +2017-02-16,aapl,lx,-1,135.345 +2017-02-16,aapl,se,-1,135.345 +2017-02-16,googl,sx,1,842.17 +2017-02-16,googl,le,1,842.17 +2017-02-17,nflx,sx,1,142.22 +2017-02-17,nflx,le,1,142.22 +2017-02-17,fb,lx,-1,133.53 +2017-02-17,fb,se,-1,133.53 +2017-02-17,aapl,sx,1,135.72 +2017-02-17,aapl,le,1,135.72 +2017-02-21,fb,sx,1,133.72 +2017-02-21,fb,le,1,133.72 +2017-02-22,amzn,lx,-1,855.61 +2017-02-22,amzn,se,-1,855.61 +2017-02-23,nflx,lx,-1,142.78 +2017-02-23,nflx,se,-1,142.78 +2017-02-23,fb,lx,-1,135.36 +2017-02-23,fb,se,-1,135.36 +2017-02-23,aapl,lx,-1,136.53 +2017-02-23,aapl,se,-1,136.53 +2017-02-23,googl,lx,-1,851.0 +2017-02-23,googl,se,-1,851.0 +2017-02-24,nflx,sx,1,143.25 +2017-02-24,nflx,le,1,143.25 +2017-02-24,fb,sx,1,135.44 +2017-02-24,fb,le,1,135.44 +2017-02-24,aapl,sx,1,136.66 +2017-02-24,aapl,le,1,136.66 +2017-02-27,amzn,sx,1,848.64 +2017-02-27,amzn,le,1,848.64 +2017-02-27,googl,sx,1,849.67 +2017-02-27,googl,le,1,849.67 +2017-02-28,nflx,lx,-1,142.13 +2017-02-28,nflx,se,-1,142.13 +2017-02-28,amzn,lx,-1,845.04 +2017-02-28,amzn,se,-1,845.04 +2017-02-28,fb,lx,-1,135.54 +2017-02-28,fb,se,-1,135.54 +2017-02-28,googl,lx,-1,844.93 +2017-02-28,googl,se,-1,844.93 +2017-03-01,nflx,sx,1,142.65 +2017-03-01,nflx,le,1,142.65 +2017-03-01,amzn,sx,1,853.08 +2017-03-01,amzn,le,1,853.08 +2017-03-01,fb,sx,1,137.42 +2017-03-01,fb,le,1,137.42 +2017-03-01,googl,sx,1,856.75 +2017-03-01,googl,le,1,856.75 +2017-03-02,nflx,lx,-1,139.53 +2017-03-02,nflx,se,-1,139.53 +2017-03-02,amzn,lx,-1,848.91 +2017-03-02,amzn,se,-1,848.91 +2017-03-02,fb,lx,-1,136.76 +2017-03-02,fb,se,-1,136.76 +2017-03-02,aapl,lx,-1,138.96 +2017-03-02,aapl,se,-1,138.96 +2017-03-02,googl,lx,-1,849.85 +2017-03-02,googl,se,-1,849.85 +2017-03-03,amzn,sx,1,849.88 +2017-03-03,amzn,le,1,849.88 +2017-03-03,fb,sx,1,137.17 +2017-03-03,fb,le,1,137.17 +2017-03-03,aapl,sx,1,139.78 +2017-03-03,aapl,le,1,139.78 +2017-03-06,nflx,sx,1,141.94 +2017-03-06,nflx,le,1,141.94 +2017-03-06,amzn,lx,-1,846.61 +2017-03-06,amzn,se,-1,846.61 +2017-03-06,aapl,lx,-1,139.34 +2017-03-06,aapl,se,-1,139.34 +2017-03-07,nflx,lx,-1,141.43 +2017-03-07,nflx,se,-1,141.43 +2017-03-07,fb,lx,-1,137.3 +2017-03-07,fb,se,-1,137.3 +2017-03-07,aapl,sx,1,139.52 +2017-03-07,aapl,le,1,139.52 +2017-03-07,googl,sx,1,851.15 +2017-03-07,googl,le,1,851.15 +2017-03-08,amzn,sx,1,850.5 +2017-03-08,amzn,le,1,850.5 +2017-03-08,fb,sx,1,137.72 +2017-03-08,fb,le,1,137.72 +2017-03-08,aapl,lx,-1,139.0 +2017-03-08,aapl,se,-1,139.0 +2017-03-09,nflx,sx,1,140.53 +2017-03-09,nflx,le,1,140.53 +2017-03-10,amzn,lx,-1,852.46 +2017-03-10,amzn,se,-1,852.46 +2017-03-10,aapl,sx,1,139.14 +2017-03-10,aapl,le,1,139.14 +2017-03-13,amzn,sx,1,854.59 +2017-03-13,amzn,le,1,854.59 +2017-03-14,nflx,lx,-1,143.19 +2017-03-14,nflx,se,-1,143.19 +2017-03-14,amzn,lx,-1,852.53 +2017-03-14,amzn,se,-1,852.53 +2017-03-14,fb,lx,-1,139.32 +2017-03-14,fb,se,-1,139.32 +2017-03-14,aapl,lx,-1,138.99 +2017-03-14,aapl,se,-1,138.99 +2017-03-15,nflx,sx,1,145.25 +2017-03-15,nflx,le,1,145.25 +2017-03-15,amzn,sx,1,852.97 +2017-03-15,amzn,le,1,852.97 +2017-03-15,fb,sx,1,139.72 +2017-03-15,fb,le,1,139.72 +2017-03-15,aapl,sx,1,140.46 +2017-03-15,aapl,le,1,140.46 +2017-03-16,nflx,lx,-1,144.39 +2017-03-16,nflx,se,-1,144.39 +2017-03-17,nflx,sx,1,145.11 +2017-03-17,nflx,le,1,145.11 +2017-03-17,amzn,lx,-1,852.31 +2017-03-17,amzn,se,-1,852.31 +2017-03-17,fb,lx,-1,139.84 +2017-03-17,fb,se,-1,139.84 +2017-03-17,aapl,lx,-1,139.99 +2017-03-17,aapl,se,-1,139.99 +2017-03-20,amzn,sx,1,856.97 +2017-03-20,amzn,le,1,856.97 +2017-03-20,fb,sx,1,139.94 +2017-03-20,fb,le,1,139.94 +2017-03-20,aapl,sx,1,141.46 +2017-03-20,aapl,le,1,141.46 +2017-03-20,googl,lx,-1,867.91 +2017-03-20,googl,se,-1,867.91 +2017-03-21,nflx,lx,-1,142.42 +2017-03-21,nflx,se,-1,142.42 +2017-03-21,amzn,lx,-1,843.2 +2017-03-21,amzn,se,-1,843.2 +2017-03-21,fb,lx,-1,138.51 +2017-03-21,fb,se,-1,138.51 +2017-03-21,aapl,lx,-1,139.84 +2017-03-21,aapl,se,-1,139.84 +2017-03-22,nflx,sx,1,142.65 +2017-03-22,nflx,le,1,142.65 +2017-03-22,amzn,sx,1,848.06 +2017-03-22,amzn,le,1,848.06 +2017-03-22,fb,sx,1,139.59 +2017-03-22,fb,le,1,139.59 +2017-03-22,aapl,sx,1,141.42 +2017-03-22,aapl,le,1,141.42 +2017-03-23,nflx,lx,-1,141.84 +2017-03-23,nflx,se,-1,141.84 +2017-03-23,amzn,lx,-1,847.38 +2017-03-23,amzn,se,-1,847.38 +2017-03-23,fb,lx,-1,139.53 +2017-03-23,fb,se,-1,139.53 +2017-03-23,aapl,lx,-1,140.92 +2017-03-23,aapl,se,-1,140.92 +2017-03-24,nflx,sx,1,142.02 +2017-03-24,nflx,le,1,142.02 +2017-03-24,fb,sx,1,140.34 +2017-03-24,fb,le,1,140.34 +2017-03-27,amzn,sx,1,846.82 +2017-03-27,amzn,le,1,846.82 +2017-03-27,fb,lx,-1,140.32 +2017-03-27,fb,se,-1,140.32 +2017-03-27,aapl,sx,1,140.88 +2017-03-27,aapl,le,1,140.88 +2017-03-27,googl,sx,1,838.51 +2017-03-27,googl,le,1,838.51 +2017-03-28,fb,sx,1,141.76 +2017-03-28,fb,le,1,141.76 +2017-03-30,fb,lx,-1,142.41 +2017-03-30,fb,se,-1,142.41 +2017-03-30,aapl,lx,-1,143.93 +2017-03-30,aapl,se,-1,143.93 +2017-03-30,googl,lx,-1,849.48 +2017-03-30,googl,se,-1,849.48 +2017-03-31,nflx,lx,-1,147.81 +2017-03-31,nflx,se,-1,147.81 +2017-04-03,fb,sx,1,142.28 +2017-04-03,fb,le,1,142.28 +2017-04-03,aapl,sx,1,143.7 +2017-04-03,aapl,le,1,143.7 +2017-04-03,googl,sx,1,856.75 +2017-04-03,googl,le,1,856.75 +2017-04-04,fb,lx,-1,141.73 +2017-04-04,fb,se,-1,141.73 +2017-04-04,googl,lx,-1,852.57 +2017-04-04,googl,se,-1,852.57 +2017-04-05,fb,sx,1,141.85 +2017-04-05,fb,le,1,141.85 +2017-04-05,aapl,lx,-1,144.02 +2017-04-05,aapl,se,-1,144.02 +2017-04-06,nflx,sx,1,143.74 +2017-04-06,nflx,le,1,143.74 +2017-04-06,amzn,lx,-1,898.28 +2017-04-06,amzn,se,-1,898.28 +2017-04-06,fb,lx,-1,141.17 +2017-04-06,fb,se,-1,141.17 +2017-04-07,nflx,lx,-1,143.11 +2017-04-07,nflx,se,-1,143.11 +2017-04-10,nflx,sx,1,143.85 +2017-04-10,nflx,le,1,143.85 +2017-04-10,amzn,sx,1,907.04 +2017-04-10,amzn,le,1,907.04 +2017-04-10,fb,sx,1,141.04 +2017-04-10,fb,le,1,141.04 +2017-04-11,amzn,lx,-1,902.36 +2017-04-11,amzn,se,-1,902.36 +2017-04-11,fb,lx,-1,139.92 +2017-04-11,fb,se,-1,139.92 +2017-04-12,nflx,lx,-1,143.83 +2017-04-12,nflx,se,-1,143.83 +2017-04-12,aapl,sx,1,141.8 +2017-04-12,aapl,le,1,141.8 +2017-04-12,googl,sx,1,841.46 +2017-04-12,googl,le,1,841.46 +2017-04-13,aapl,lx,-1,141.05 +2017-04-13,aapl,se,-1,141.05 +2017-04-13,googl,lx,-1,840.18 +2017-04-13,googl,se,-1,840.18 +2017-04-17,nflx,sx,1,147.25 +2017-04-17,nflx,le,1,147.25 +2017-04-17,amzn,sx,1,901.99 +2017-04-17,amzn,le,1,901.99 +2017-04-17,fb,sx,1,141.42 +2017-04-17,fb,le,1,141.42 +2017-04-17,aapl,sx,1,141.83 +2017-04-17,aapl,le,1,141.83 +2017-04-17,googl,sx,1,855.13 +2017-04-17,googl,le,1,855.13 +2017-04-18,nflx,lx,-1,143.36 +2017-04-18,nflx,se,-1,143.36 +2017-04-18,fb,lx,-1,140.96 +2017-04-18,fb,se,-1,140.96 +2017-04-18,aapl,lx,-1,141.2 +2017-04-18,aapl,se,-1,141.2 +2017-04-18,googl,lx,-1,853.99 +2017-04-18,googl,se,-1,853.99 +2017-04-19,amzn,lx,-1,899.2 +2017-04-19,amzn,se,-1,899.2 +2017-04-19,fb,sx,1,142.27 +2017-04-19,fb,le,1,142.27 +2017-04-19,googl,sx,1,856.51 +2017-04-19,googl,le,1,856.51 +2017-04-20,nflx,sx,1,141.18 +2017-04-20,nflx,le,1,141.18 +2017-04-20,amzn,sx,1,902.06 +2017-04-20,amzn,le,1,902.06 +2017-04-20,aapl,sx,1,142.44 +2017-04-20,aapl,le,1,142.44 +2017-04-21,amzn,lx,-1,898.53 +2017-04-21,amzn,se,-1,898.53 +2017-04-21,fb,lx,-1,143.68 +2017-04-21,fb,se,-1,143.68 +2017-04-21,aapl,lx,-1,142.27 +2017-04-21,aapl,se,-1,142.27 +2017-04-21,googl,lx,-1,858.95 +2017-04-21,googl,se,-1,858.95 +2017-04-24,amzn,sx,1,907.41 +2017-04-24,amzn,le,1,907.41 +2017-04-24,fb,sx,1,145.47 +2017-04-24,fb,le,1,145.47 +2017-04-24,aapl,sx,1,143.64 +2017-04-24,aapl,le,1,143.64 +2017-04-24,googl,sx,1,878.93 +2017-04-24,googl,le,1,878.93 +2017-04-26,nflx,lx,-1,150.17 +2017-04-26,nflx,se,-1,150.17 +2017-04-26,aapl,lx,-1,143.6508 +2017-04-26,aapl,se,-1,143.6508 +2017-04-27,nflx,sx,1,153.08 +2017-04-27,nflx,le,1,153.08 +2017-04-27,aapl,sx,1,143.79 +2017-04-27,aapl,le,1,143.79 +2017-04-28,nflx,lx,-1,152.2 +2017-04-28,nflx,se,-1,152.2 +2017-04-28,aapl,lx,-1,143.65 +2017-04-28,aapl,se,-1,143.65 +2017-05-01,nflx,sx,1,155.35 +2017-05-01,nflx,le,1,155.35 +2017-05-01,aapl,sx,1,146.6 +2017-05-01,aapl,le,1,146.6 +2017-05-02,amzn,lx,-1,946.68 +2017-05-02,amzn,se,-1,946.68 +2017-05-03,nflx,lx,-1,155.59 +2017-05-03,nflx,se,-1,155.59 +2017-05-03,fb,lx,-1,151.8 +2017-05-03,fb,se,-1,151.8 +2017-05-03,aapl,lx,-1,147.06 +2017-05-03,aapl,se,-1,147.06 +2017-05-04,nflx,sx,1,157.25 +2017-05-04,nflx,le,1,157.25 +2017-05-05,nflx,lx,-1,156.6 +2017-05-05,nflx,se,-1,156.6 +2017-05-05,aapl,sx,1,148.96 +2017-05-05,aapl,le,1,148.96 +2017-05-05,googl,lx,-1,950.28 +2017-05-05,googl,se,-1,950.28 +2017-05-08,amzn,sx,1,949.04 +2017-05-08,amzn,le,1,949.04 +2017-05-08,fb,sx,1,151.06 +2017-05-08,fb,le,1,151.06 +2017-05-08,googl,sx,1,958.69 +2017-05-08,googl,le,1,958.69 +2017-05-09,nflx,sx,1,157.46 +2017-05-09,nflx,le,1,157.46 +2017-05-09,fb,lx,-1,150.48 +2017-05-09,fb,se,-1,150.48 +2017-05-09,googl,lx,-1,956.71 +2017-05-09,googl,se,-1,956.71 +2017-05-10,amzn,lx,-1,948.95 +2017-05-10,amzn,se,-1,948.95 +2017-05-10,aapl,lx,-1,153.26 +2017-05-10,aapl,se,-1,153.26 +2017-05-11,nflx,lx,-1,158.54 +2017-05-11,nflx,se,-1,158.54 +2017-05-11,aapl,sx,1,153.95 +2017-05-11,aapl,le,1,153.95 +2017-05-11,googl,sx,1,955.89 +2017-05-11,googl,le,1,955.89 +2017-05-12,nflx,sx,1,160.81 +2017-05-12,nflx,le,1,160.81 +2017-05-12,amzn,sx,1,961.35 +2017-05-12,amzn,le,1,961.35 +2017-05-12,fb,sx,1,150.33 +2017-05-12,fb,le,1,150.33 +2017-05-12,googl,lx,-1,955.14 +2017-05-12,googl,se,-1,955.14 +2017-05-15,nflx,lx,-1,160.02 +2017-05-15,nflx,se,-1,160.02 +2017-05-15,amzn,lx,-1,957.97 +2017-05-15,amzn,se,-1,957.97 +2017-05-15,fb,lx,-1,150.19 +2017-05-15,fb,se,-1,150.19 +2017-05-15,aapl,lx,-1,155.7 +2017-05-15,aapl,se,-1,155.7 +2017-05-15,googl,sx,1,959.22 +2017-05-15,googl,le,1,959.22 +2017-05-16,amzn,sx,1,966.07 +2017-05-16,amzn,le,1,966.07 +2017-05-17,amzn,lx,-1,944.52 +2017-05-17,amzn,se,-1,944.52 +2017-05-17,googl,lx,-1,942.17 +2017-05-17,googl,se,-1,942.17 +2017-05-18,nflx,sx,1,155.7 +2017-05-18,nflx,le,1,155.7 +2017-05-18,amzn,sx,1,958.49 +2017-05-18,amzn,le,1,958.49 +2017-05-18,fb,sx,1,147.66 +2017-05-18,fb,le,1,147.66 +2017-05-18,aapl,sx,1,152.54 +2017-05-18,aapl,le,1,152.54 +2017-05-18,googl,sx,1,950.5 +2017-05-18,googl,le,1,950.5 +2017-05-23,fb,lx,-1,148.07 +2017-05-23,fb,se,-1,148.07 +2017-05-23,aapl,lx,-1,153.8 +2017-05-23,aapl,se,-1,153.8 +2017-05-24,nflx,lx,-1,157.75 +2017-05-24,nflx,se,-1,157.75 +2017-05-24,fb,sx,1,150.04 +2017-05-24,fb,le,1,150.04 +2017-05-25,nflx,sx,1,163.05 +2017-05-25,nflx,le,1,163.05 +2017-05-25,aapl,sx,1,153.87 +2017-05-25,aapl,le,1,153.87 +2017-05-26,nflx,lx,-1,162.43 +2017-05-26,nflx,se,-1,162.43 +2017-05-26,aapl,lx,-1,153.61 +2017-05-26,aapl,se,-1,153.61 +2017-05-30,nflx,sx,1,163.22 +2017-05-30,nflx,le,1,163.22 +2017-05-30,aapl,sx,1,153.67 +2017-05-30,aapl,le,1,153.67 +2017-05-31,nflx,lx,-1,163.07 +2017-05-31,nflx,se,-1,163.07 +2017-05-31,amzn,lx,-1,994.62 +2017-05-31,amzn,se,-1,994.62 +2017-05-31,fb,lx,-1,151.46 +2017-05-31,fb,se,-1,151.46 +2017-05-31,aapl,lx,-1,152.76 +2017-05-31,aapl,se,-1,152.76 +2017-05-31,googl,lx,-1,987.09 +2017-05-31,googl,se,-1,987.09 +2017-06-01,amzn,sx,1,995.95 +2017-06-01,amzn,le,1,995.95 +2017-06-01,fb,sx,1,151.53 +2017-06-01,fb,le,1,151.53 +2017-06-01,aapl,sx,1,153.18 +2017-06-01,aapl,le,1,153.18 +2017-06-01,googl,sx,1,988.29 +2017-06-01,googl,le,1,988.29 +2017-06-02,nflx,sx,1,165.18 +2017-06-02,nflx,le,1,165.18 +2017-06-05,nflx,lx,-1,165.06 +2017-06-05,nflx,se,-1,165.06 +2017-06-05,aapl,lx,-1,153.93 +2017-06-05,aapl,se,-1,153.93 +2017-06-06,nflx,sx,1,165.17 +2017-06-06,nflx,le,1,165.17 +2017-06-06,amzn,lx,-1,1002.97 +2017-06-06,amzn,se,-1,1002.97 +2017-06-06,fb,lx,-1,152.81 +2017-06-06,fb,se,-1,152.81 +2017-06-06,aapl,sx,1,154.45 +2017-06-06,aapl,le,1,154.45 +2017-06-06,googl,lx,-1,996.68 +2017-06-06,googl,se,-1,996.68 +2017-06-07,amzn,sx,1,1010.07 +2017-06-07,amzn,le,1,1010.07 +2017-06-07,fb,sx,1,153.12 +2017-06-07,fb,le,1,153.12 +2017-06-07,googl,sx,1,1001.59 +2017-06-07,googl,le,1,1001.59 +2017-06-08,amzn,lx,-1,1009.94 +2017-06-08,amzn,se,-1,1009.94 +2017-06-08,aapl,lx,-1,154.99 +2017-06-08,aapl,se,-1,154.99 +2017-06-09,nflx,lx,-1,158.03 +2017-06-09,nflx,se,-1,158.03 +2017-06-09,fb,lx,-1,149.63 +2017-06-09,fb,se,-1,149.63 +2017-06-09,googl,lx,-1,970.12 +2017-06-09,googl,se,-1,970.12 +2017-06-13,nflx,sx,1,152.72 +2017-06-13,nflx,le,1,152.72 +2017-06-13,amzn,sx,1,980.79 +2017-06-13,amzn,le,1,980.79 +2017-06-13,fb,sx,1,150.68 +2017-06-13,fb,le,1,150.68 +2017-06-13,aapl,sx,1,146.59 +2017-06-13,aapl,le,1,146.59 +2017-06-13,googl,sx,1,970.5 +2017-06-13,googl,le,1,970.5 +2017-06-14,nflx,lx,-1,152.2 +2017-06-14,nflx,se,-1,152.2 +2017-06-14,amzn,lx,-1,976.47 +2017-06-14,amzn,se,-1,976.47 +2017-06-14,fb,lx,-1,150.25 +2017-06-14,fb,se,-1,150.25 +2017-06-14,aapl,lx,-1,145.16 +2017-06-14,aapl,se,-1,145.16 +2017-06-14,googl,lx,-1,967.93 +2017-06-14,googl,se,-1,967.93 +2017-06-16,nflx,sx,1,152.38 +2017-06-16,nflx,le,1,152.38 +2017-06-16,amzn,sx,1,987.71 +2017-06-16,amzn,le,1,987.71 +2017-06-16,fb,sx,1,150.64 +2017-06-16,fb,le,1,150.64 +2017-06-19,aapl,sx,1,146.34 +2017-06-19,aapl,le,1,146.34 +2017-06-19,googl,sx,1,975.22 +2017-06-19,googl,le,1,975.22 +2017-06-20,nflx,lx,-1,152.05 +2017-06-20,nflx,se,-1,152.05 +2017-06-20,amzn,lx,-1,992.59 +2017-06-20,amzn,se,-1,992.59 +2017-06-20,fb,lx,-1,152.25 +2017-06-20,fb,se,-1,152.25 +2017-06-20,aapl,lx,-1,145.01 +2017-06-20,aapl,se,-1,145.01 +2017-06-20,googl,lx,-1,968.99 +2017-06-20,googl,se,-1,968.99 +2017-06-21,nflx,sx,1,155.03 +2017-06-21,nflx,le,1,155.03 +2017-06-21,amzn,sx,1,1002.23 +2017-06-21,amzn,le,1,1002.23 +2017-06-21,fb,sx,1,153.91 +2017-06-21,fb,le,1,153.91 +2017-06-21,aapl,sx,1,145.87 +2017-06-21,aapl,le,1,145.87 +2017-06-21,googl,sx,1,978.59 +2017-06-21,googl,le,1,978.59 +2017-06-22,nflx,lx,-1,154.89 +2017-06-22,nflx,se,-1,154.89 +2017-06-22,amzn,lx,-1,1001.3 +2017-06-22,amzn,se,-1,1001.3 +2017-06-22,fb,lx,-1,153.4 +2017-06-22,fb,se,-1,153.4 +2017-06-22,aapl,lx,-1,145.63 +2017-06-22,aapl,se,-1,145.63 +2017-06-22,googl,lx,-1,976.62 +2017-06-22,googl,se,-1,976.62 +2017-06-23,nflx,sx,1,158.03 +2017-06-23,nflx,le,1,158.03 +2017-06-23,amzn,sx,1,1003.74 +2017-06-23,amzn,le,1,1003.74 +2017-06-23,fb,sx,1,155.07 +2017-06-23,fb,le,1,155.07 +2017-06-23,aapl,sx,1,146.35 +2017-06-23,aapl,le,1,146.35 +2017-06-23,googl,sx,1,986.09 +2017-06-23,googl,le,1,986.09 +2017-06-26,nflx,lx,-1,157.5 +2017-06-26,nflx,se,-1,157.5 +2017-06-26,amzn,lx,-1,993.98 +2017-06-26,amzn,se,-1,993.98 +2017-06-26,fb,lx,-1,153.59 +2017-06-26,fb,se,-1,153.59 +2017-06-26,aapl,lx,-1,145.82 +2017-06-26,aapl,se,-1,145.82 +2017-06-26,googl,lx,-1,972.09 +2017-06-26,googl,se,-1,972.09 +2017-06-28,nflx,sx,1,153.41 +2017-06-28,nflx,le,1,153.41 +2017-06-28,amzn,sx,1,990.33 +2017-06-28,amzn,le,1,990.33 +2017-06-28,fb,sx,1,153.24 +2017-06-28,fb,le,1,153.24 +2017-06-28,aapl,sx,1,145.83 +2017-06-28,aapl,le,1,145.83 +2017-06-28,googl,sx,1,961.01 +2017-06-28,googl,le,1,961.01 +2017-06-29,nflx,lx,-1,150.09 +2017-06-29,nflx,se,-1,150.09 +2017-06-29,amzn,lx,-1,975.93 +2017-06-29,amzn,se,-1,975.93 +2017-06-29,fb,lx,-1,151.04 +2017-06-29,fb,se,-1,151.04 +2017-06-29,aapl,lx,-1,143.68 +2017-06-29,aapl,se,-1,143.68 +2017-06-29,googl,lx,-1,937.82 +2017-06-29,googl,se,-1,937.82 +2017-06-30,aapl,sx,1,144.02 +2017-06-30,aapl,le,1,144.02 +2017-07-03,aapl,lx,-1,143.5 +2017-07-03,aapl,se,-1,143.5 +2017-07-05,nflx,sx,1,147.61 +2017-07-05,nflx,le,1,147.61 +2017-07-05,amzn,sx,1,971.4 +2017-07-05,amzn,le,1,971.4 +2017-07-05,fb,sx,1,150.34 +2017-07-05,fb,le,1,150.34 +2017-07-05,aapl,sx,1,144.09 +2017-07-05,aapl,le,1,144.09 +2017-07-05,googl,sx,1,932.26 +2017-07-05,googl,le,1,932.26 +2017-07-06,nflx,lx,-1,146.25 +2017-07-06,nflx,se,-1,146.25 +2017-07-06,amzn,lx,-1,965.14 +2017-07-06,amzn,se,-1,965.14 +2017-07-06,fb,lx,-1,148.82 +2017-07-06,fb,se,-1,148.82 +2017-07-06,aapl,lx,-1,142.73 +2017-07-06,aapl,se,-1,142.73 +2017-07-06,googl,lx,-1,927.69 +2017-07-06,googl,se,-1,927.69 +2017-07-07,nflx,sx,1,150.18 +2017-07-07,nflx,le,1,150.18 +2017-07-07,amzn,sx,1,978.76 +2017-07-07,amzn,le,1,978.76 +2017-07-07,fb,sx,1,151.44 +2017-07-07,fb,le,1,151.44 +2017-07-07,aapl,sx,1,144.18 +2017-07-07,aapl,le,1,144.18 +2017-07-07,googl,sx,1,940.81 +2017-07-07,googl,le,1,940.81 +2017-07-11,amzn,lx,-1,994.13 +2017-07-11,amzn,se,-1,994.13 +2017-07-12,amzn,sx,1,1006.51 +2017-07-12,amzn,le,1,1006.51 +2017-07-13,nflx,lx,-1,158.17 +2017-07-13,nflx,se,-1,158.17 +2017-07-13,amzn,lx,-1,999.855 +2017-07-13,amzn,se,-1,999.855 +2017-07-14,nflx,sx,1,161.12 +2017-07-14,nflx,le,1,161.12 +2017-07-14,amzn,sx,1,1001.81 +2017-07-14,amzn,le,1,1001.81 +2017-07-17,fb,lx,-1,159.73 +2017-07-17,fb,se,-1,159.73 +2017-07-17,googl,lx,-1,975.96 +2017-07-17,googl,se,-1,975.96 +2017-07-18,fb,sx,1,162.86 +2017-07-18,fb,le,1,162.86 +2017-07-18,googl,sx,1,986.95 +2017-07-18,googl,le,1,986.95 +2017-07-20,nflx,lx,-1,183.6 +2017-07-20,nflx,se,-1,183.6 +2017-07-20,aapl,lx,-1,150.34 +2017-07-20,aapl,se,-1,150.34 +2017-07-20,googl,lx,-1,992.19 +2017-07-20,googl,se,-1,992.19 +2017-07-21,nflx,sx,1,188.54 +2017-07-21,nflx,le,1,188.54 +2017-07-21,amzn,lx,-1,1025.67 +2017-07-21,amzn,se,-1,1025.67 +2017-07-21,fb,lx,-1,164.43 +2017-07-21,fb,se,-1,164.43 +2017-07-21,googl,sx,1,993.84 +2017-07-21,googl,le,1,993.84 +2017-07-24,nflx,lx,-1,187.91 +2017-07-24,nflx,se,-1,187.91 +2017-07-24,amzn,sx,1,1038.95 +2017-07-24,amzn,le,1,1038.95 +2017-07-24,fb,sx,1,166.0 +2017-07-24,fb,le,1,166.0 +2017-07-24,aapl,sx,1,152.09 +2017-07-24,aapl,le,1,152.09 +2017-07-25,fb,lx,-1,165.28 +2017-07-25,fb,se,-1,165.28 +2017-07-25,googl,lx,-1,969.03 +2017-07-25,googl,se,-1,969.03 +2017-07-26,nflx,sx,1,189.08 +2017-07-26,nflx,le,1,189.08 +2017-07-26,fb,sx,1,165.61 +2017-07-26,fb,le,1,165.61 +2017-07-27,nflx,lx,-1,182.68 +2017-07-27,nflx,se,-1,182.68 +2017-07-27,amzn,lx,-1,1046.0 +2017-07-27,amzn,se,-1,1046.0 +2017-07-27,aapl,lx,-1,150.56 +2017-07-27,aapl,se,-1,150.56 +2017-07-28,nflx,sx,1,184.04 +2017-07-28,nflx,le,1,184.04 +2017-07-28,googl,sx,1,958.33 +2017-07-28,googl,le,1,958.33 +2017-07-31,nflx,lx,-1,181.66 +2017-07-31,nflx,se,-1,181.66 +2017-07-31,fb,lx,-1,169.25 +2017-07-31,fb,se,-1,169.25 +2017-07-31,googl,lx,-1,945.5 +2017-07-31,googl,se,-1,945.5 +2017-08-01,nflx,sx,1,182.03 +2017-08-01,nflx,le,1,182.03 +2017-08-01,amzn,sx,1,996.19 +2017-08-01,amzn,le,1,996.19 +2017-08-01,fb,sx,1,169.86 +2017-08-01,fb,le,1,169.86 +2017-08-01,aapl,sx,1,150.05 +2017-08-01,aapl,le,1,150.05 +2017-08-01,googl,sx,1,946.56 +2017-08-01,googl,le,1,946.56 +2017-08-02,nflx,lx,-1,180.74 +2017-08-02,nflx,se,-1,180.74 +2017-08-02,amzn,lx,-1,995.89 +2017-08-02,amzn,se,-1,995.89 +2017-08-02,fb,lx,-1,169.25 +2017-08-02,fb,se,-1,169.25 +2017-08-03,aapl,lx,-1,155.57 +2017-08-03,aapl,se,-1,155.57 +2017-08-03,googl,lx,-1,940.3 +2017-08-03,googl,se,-1,940.3 +2017-08-04,nflx,sx,1,180.27 +2017-08-04,nflx,le,1,180.27 +2017-08-04,amzn,sx,1,987.58 +2017-08-04,amzn,le,1,987.58 +2017-08-04,fb,sx,1,169.62 +2017-08-04,fb,le,1,169.62 +2017-08-04,aapl,sx,1,156.39 +2017-08-04,aapl,le,1,156.39 +2017-08-04,googl,sx,1,945.79 +2017-08-04,googl,le,1,945.79 +2017-08-07,googl,lx,-1,945.75 +2017-08-07,googl,se,-1,945.75 +2017-08-08,nflx,lx,-1,178.36 +2017-08-08,nflx,se,-1,178.36 +2017-08-08,fb,lx,-1,171.23 +2017-08-08,fb,se,-1,171.23 +2017-08-09,amzn,lx,-1,982.01 +2017-08-09,amzn,se,-1,982.01 +2017-08-10,aapl,lx,-1,155.27 +2017-08-10,aapl,se,-1,155.27 +2017-08-11,nflx,sx,1,171.4 +2017-08-11,nflx,le,1,171.4 +2017-08-11,amzn,sx,1,967.99 +2017-08-11,amzn,le,1,967.99 +2017-08-11,fb,sx,1,168.08 +2017-08-11,fb,le,1,168.08 +2017-08-11,aapl,sx,1,157.48 +2017-08-11,aapl,le,1,157.48 +2017-08-11,googl,sx,1,930.09 +2017-08-11,googl,le,1,930.09 +2017-08-14,nflx,lx,-1,171.0 +2017-08-14,nflx,se,-1,171.0 +2017-08-15,amzn,lx,-1,982.74 +2017-08-15,amzn,se,-1,982.74 +2017-08-15,googl,lx,-1,938.08 +2017-08-15,googl,se,-1,938.08 +2017-08-16,nflx,sx,1,169.98 +2017-08-16,nflx,le,1,169.98 +2017-08-16,fb,lx,-1,170.0 +2017-08-16,fb,se,-1,170.0 +2017-08-16,aapl,lx,-1,160.95 +2017-08-16,aapl,se,-1,160.95 +2017-08-16,googl,sx,1,944.27 +2017-08-16,googl,le,1,944.27 +2017-08-17,nflx,lx,-1,166.09 +2017-08-17,nflx,se,-1,166.09 +2017-08-17,googl,lx,-1,927.66 +2017-08-17,googl,se,-1,927.66 +2017-08-18,nflx,sx,1,166.54 +2017-08-18,nflx,le,1,166.54 +2017-08-18,fb,sx,1,167.41 +2017-08-18,fb,le,1,167.41 +2017-08-22,amzn,sx,1,966.9 +2017-08-22,amzn,le,1,966.9 +2017-08-22,aapl,sx,1,159.78 +2017-08-22,aapl,le,1,159.78 +2017-08-22,googl,sx,1,940.4 +2017-08-22,googl,le,1,940.4 +2017-08-23,nflx,lx,-1,169.06 +2017-08-23,nflx,se,-1,169.06 +2017-08-23,amzn,lx,-1,958.0 +2017-08-23,amzn,se,-1,958.0 +2017-08-23,fb,lx,-1,168.71 +2017-08-23,fb,se,-1,168.71 +2017-08-24,aapl,lx,-1,159.27 +2017-08-24,aapl,se,-1,159.27 +2017-08-24,googl,lx,-1,936.89 +2017-08-24,googl,se,-1,936.89 +2017-08-25,aapl,sx,1,159.86 +2017-08-25,aapl,le,1,159.86 +2017-08-28,nflx,sx,1,167.12 +2017-08-28,nflx,le,1,167.12 +2017-08-28,amzn,sx,1,946.02 +2017-08-28,amzn,le,1,946.02 +2017-08-28,fb,sx,1,167.24 +2017-08-28,fb,le,1,167.24 +2017-08-29,googl,sx,1,935.75 +2017-08-29,googl,le,1,935.75 +2017-09-01,amzn,lx,-1,978.25 +2017-09-01,amzn,se,-1,978.25 +2017-09-01,googl,lx,-1,951.99 +2017-09-01,googl,se,-1,951.99 +2017-09-05,nflx,lx,-1,174.52 +2017-09-05,nflx,se,-1,174.52 +2017-09-05,fb,lx,-1,170.72 +2017-09-05,fb,se,-1,170.72 +2017-09-05,aapl,lx,-1,162.08 +2017-09-05,aapl,se,-1,162.08 +2017-09-06,nflx,sx,1,179.25 +2017-09-06,nflx,le,1,179.25 +2017-09-06,amzn,sx,1,967.8 +2017-09-06,amzn,le,1,967.8 +2017-09-06,fb,sx,1,172.09 +2017-09-06,fb,le,1,172.09 +2017-09-06,googl,sx,1,942.02 +2017-09-06,googl,le,1,942.02 +2017-09-07,nflx,lx,-1,179.0 +2017-09-07,nflx,se,-1,179.0 +2017-09-08,amzn,lx,-1,965.9 +2017-09-08,amzn,se,-1,965.9 +2017-09-08,fb,lx,-1,170.95 +2017-09-08,fb,se,-1,170.95 +2017-09-08,googl,lx,-1,941.41 +2017-09-08,googl,se,-1,941.41 +2017-09-11,nflx,sx,1,181.74 +2017-09-11,nflx,le,1,181.74 +2017-09-11,amzn,sx,1,977.96 +2017-09-11,amzn,le,1,977.96 +2017-09-11,fb,sx,1,173.51 +2017-09-11,fb,le,1,173.51 +2017-09-11,aapl,sx,1,161.5 +2017-09-11,aapl,le,1,161.5 +2017-09-11,googl,sx,1,943.29 +2017-09-11,googl,le,1,943.29 +2017-09-12,fb,lx,-1,172.96 +2017-09-12,fb,se,-1,172.96 +2017-09-12,aapl,lx,-1,160.82 +2017-09-12,aapl,se,-1,160.82 +2017-09-13,nflx,lx,-1,183.64 +2017-09-13,nflx,se,-1,183.64 +2017-09-13,fb,sx,1,173.05 +2017-09-13,fb,le,1,173.05 +2017-09-14,amzn,lx,-1,992.21 +2017-09-14,amzn,se,-1,992.21 +2017-09-14,fb,lx,-1,170.96 +2017-09-14,fb,se,-1,170.96 +2017-09-14,googl,lx,-1,940.13 +2017-09-14,googl,se,-1,940.13 +2017-09-15,fb,sx,1,171.58 +2017-09-15,fb,le,1,171.58 +2017-09-15,aapl,sx,1,159.88 +2017-09-15,aapl,le,1,159.88 +2017-09-18,nflx,sx,1,184.62 +2017-09-18,nflx,le,1,184.62 +2017-09-18,fb,lx,-1,170.01 +2017-09-18,fb,se,-1,170.01 +2017-09-18,aapl,lx,-1,158.67 +2017-09-18,aapl,se,-1,158.67 +2017-09-19,fb,sx,1,172.52 +2017-09-19,fb,le,1,172.52 +2017-09-19,aapl,sx,1,158.73 +2017-09-19,aapl,le,1,158.73 +2017-09-19,googl,sx,1,936.86 +2017-09-19,googl,le,1,936.86 +2017-09-20,nflx,lx,-1,185.51 +2017-09-20,nflx,se,-1,185.51 +2017-09-20,amzn,sx,1,973.21 +2017-09-20,amzn,le,1,973.21 +2017-09-20,fb,lx,-1,172.17 +2017-09-20,fb,se,-1,172.17 +2017-09-20,aapl,lx,-1,156.07 +2017-09-20,aapl,se,-1,156.07 +2017-09-21,nflx,sx,1,188.78 +2017-09-21,nflx,le,1,188.78 +2017-09-21,amzn,lx,-1,964.65 +2017-09-21,amzn,se,-1,964.65 +2017-09-22,nflx,lx,-1,187.35 +2017-09-22,nflx,se,-1,187.35 +2017-09-22,googl,lx,-1,943.26 +2017-09-22,googl,se,-1,943.26 +2017-09-26,nflx,sx,1,179.38 +2017-09-26,nflx,le,1,179.38 +2017-09-26,fb,sx,1,164.21 +2017-09-26,fb,le,1,164.21 +2017-09-26,aapl,sx,1,153.14 +2017-09-26,aapl,le,1,153.14 +2017-09-26,googl,sx,1,937.43 +2017-09-26,googl,le,1,937.43 +2017-09-27,amzn,sx,1,950.87 +2017-09-27,amzn,le,1,950.87 +2017-09-28,nflx,lx,-1,180.7 +2017-09-28,nflx,se,-1,180.7 +2017-09-28,aapl,lx,-1,153.28 +2017-09-28,aapl,se,-1,153.28 +2017-09-29,nflx,sx,1,181.35 +2017-09-29,nflx,le,1,181.35 +2017-09-29,aapl,sx,1,154.12 +2017-09-29,aapl,le,1,154.12 +2017-10-02,nflx,lx,-1,177.01 +2017-10-02,nflx,se,-1,177.01 +2017-10-02,amzn,lx,-1,959.19 +2017-10-02,amzn,se,-1,959.19 +2017-10-02,fb,lx,-1,169.47 +2017-10-02,fb,se,-1,169.47 +2017-10-02,aapl,lx,-1,153.81 +2017-10-02,aapl,se,-1,153.81 +2017-10-02,googl,lx,-1,967.47 +2017-10-02,googl,se,-1,967.47 +2017-10-03,nflx,sx,1,179.19 +2017-10-03,nflx,le,1,179.19 +2017-10-03,fb,sx,1,169.96 +2017-10-03,fb,le,1,169.96 +2017-10-03,aapl,sx,1,154.48 +2017-10-03,aapl,le,1,154.48 +2017-10-03,googl,sx,1,972.08 +2017-10-03,googl,le,1,972.08 +2017-10-04,amzn,sx,1,965.45 +2017-10-04,amzn,le,1,965.45 +2017-10-04,fb,lx,-1,168.42 +2017-10-04,fb,se,-1,168.42 +2017-10-04,aapl,lx,-1,153.4508 +2017-10-04,aapl,se,-1,153.4508 +2017-10-04,googl,lx,-1,966.78 +2017-10-04,googl,se,-1,966.78 +2017-10-05,fb,sx,1,171.24 +2017-10-05,fb,le,1,171.24 +2017-10-05,aapl,sx,1,155.39 +2017-10-05,aapl,le,1,155.39 +2017-10-05,googl,sx,1,985.19 +2017-10-05,googl,le,1,985.19 +2017-10-06,aapl,lx,-1,155.3 +2017-10-06,aapl,se,-1,155.3 +2017-10-09,nflx,lx,-1,196.87 +2017-10-09,nflx,se,-1,196.87 +2017-10-09,aapl,sx,1,155.84 +2017-10-09,aapl,le,1,155.84 +2017-10-09,googl,lx,-1,992.31 +2017-10-09,googl,se,-1,992.31 +2017-10-10,amzn,lx,-1,987.2 +2017-10-10,amzn,se,-1,987.2 +2017-10-10,fb,lx,-1,171.59 +2017-10-10,fb,se,-1,171.59 +2017-10-11,amzn,sx,1,995.0 +2017-10-11,amzn,le,1,995.0 +2017-10-11,fb,sx,1,172.74 +2017-10-11,fb,le,1,172.74 +2017-10-11,googl,sx,1,1005.65 +2017-10-11,googl,le,1,1005.65 +2017-10-12,nflx,sx,1,195.86 +2017-10-12,nflx,le,1,195.86 +2017-10-12,fb,lx,-1,172.55 +2017-10-12,fb,se,-1,172.55 +2017-10-12,aapl,lx,-1,156.0 +2017-10-12,aapl,se,-1,156.0 +2017-10-13,fb,sx,1,173.74 +2017-10-13,fb,le,1,173.74 +2017-10-13,aapl,sx,1,156.99 +2017-10-13,aapl,le,1,156.99 +2017-10-17,nflx,lx,-1,199.48 +2017-10-17,nflx,se,-1,199.48 +2017-10-18,amzn,lx,-1,997.0 +2017-10-18,amzn,se,-1,997.0 +2017-10-18,fb,lx,-1,176.03 +2017-10-18,fb,se,-1,176.03 +2017-10-18,aapl,lx,-1,159.76 +2017-10-18,aapl,se,-1,159.76 +2017-10-19,googl,lx,-1,1001.84 +2017-10-19,googl,se,-1,1001.84 +2017-10-20,fb,sx,1,174.98 +2017-10-20,fb,le,1,174.98 +2017-10-20,aapl,sx,1,156.16 +2017-10-20,aapl,le,1,156.16 +2017-10-20,googl,sx,1,1005.07 +2017-10-20,googl,le,1,1005.07 +2017-10-23,fb,lx,-1,171.27 +2017-10-23,fb,se,-1,171.27 +2017-10-23,googl,lx,-1,985.54 +2017-10-23,googl,se,-1,985.54 +2017-10-24,nflx,sx,1,196.02 +2017-10-24,nflx,le,1,196.02 +2017-10-24,amzn,sx,1,975.9 +2017-10-24,amzn,le,1,975.9 +2017-10-24,fb,sx,1,171.8 +2017-10-24,fb,le,1,171.8 +2017-10-24,googl,sx,1,988.49 +2017-10-24,googl,le,1,988.49 +2017-10-25,nflx,lx,-1,193.77 +2017-10-25,nflx,se,-1,193.77 +2017-10-25,amzn,lx,-1,972.91 +2017-10-25,amzn,se,-1,972.91 +2017-10-25,fb,lx,-1,170.6 +2017-10-25,fb,se,-1,170.6 +2017-10-25,aapl,lx,-1,156.405 +2017-10-25,aapl,se,-1,156.405 +2017-10-26,nflx,sx,1,195.21 +2017-10-26,nflx,le,1,195.21 +2017-10-26,fb,sx,1,170.63 +2017-10-26,fb,le,1,170.63 +2017-10-26,aapl,sx,1,157.41 +2017-10-26,aapl,le,1,157.41 +2017-10-26,googl,lx,-1,991.42 +2017-10-26,googl,se,-1,991.42 +2017-10-27,amzn,sx,1,1100.95 +2017-10-27,amzn,le,1,1100.95 +2017-10-27,googl,sx,1,1033.67 +2017-10-27,googl,le,1,1033.67 +2017-10-30,nflx,lx,-1,198.37 +2017-10-30,nflx,se,-1,198.37 +2017-10-30,googl,lx,-1,1033.13 +2017-10-30,googl,se,-1,1033.13 +2017-10-31,amzn,lx,-1,1105.28 +2017-10-31,amzn,se,-1,1105.28 +2017-11-01,nflx,sx,1,198.0 +2017-11-01,nflx,le,1,198.0 +2017-11-01,aapl,lx,-1,166.89 +2017-11-01,aapl,se,-1,166.89 +2017-11-01,googl,sx,1,1042.6 +2017-11-01,googl,le,1,1042.6 +2017-11-02,fb,lx,-1,178.92 +2017-11-02,fb,se,-1,178.92 +2017-11-02,aapl,sx,1,168.11 +2017-11-02,aapl,le,1,168.11 +2017-11-03,amzn,sx,1,1111.6 +2017-11-03,amzn,le,1,1111.6 +2017-11-06,fb,sx,1,180.17 +2017-11-06,fb,le,1,180.17 +2017-11-06,googl,lx,-1,1042.68 +2017-11-06,googl,se,-1,1042.68 +2017-11-07,nflx,lx,-1,195.89 +2017-11-07,nflx,se,-1,195.89 +2017-11-07,googl,sx,1,1052.39 +2017-11-07,googl,le,1,1052.39 +2017-11-09,fb,lx,-1,179.3 +2017-11-09,fb,se,-1,179.3 +2017-11-09,googl,lx,-1,1047.72 +2017-11-09,googl,se,-1,1047.72 +2017-11-10,amzn,lx,-1,1125.35 +2017-11-10,amzn,se,-1,1125.35 +2017-11-10,aapl,lx,-1,174.67 +2017-11-10,aapl,se,-1,174.67 +2017-11-13,nflx,sx,1,195.08 +2017-11-13,nflx,le,1,195.08 +2017-11-13,amzn,sx,1,1129.17 +2017-11-13,amzn,le,1,1129.17 +2017-11-13,fb,sx,1,178.77 +2017-11-13,fb,le,1,178.77 +2017-11-14,fb,lx,-1,178.07 +2017-11-14,fb,se,-1,178.07 +2017-11-14,googl,sx,1,1041.64 +2017-11-14,googl,le,1,1041.64 +2017-11-15,nflx,lx,-1,192.12 +2017-11-15,nflx,se,-1,192.12 +2017-11-15,amzn,lx,-1,1126.69 +2017-11-15,amzn,se,-1,1126.69 +2017-11-15,googl,lx,-1,1036.41 +2017-11-15,googl,se,-1,1036.41 +2017-11-16,nflx,sx,1,195.51 +2017-11-16,nflx,le,1,195.51 +2017-11-16,amzn,sx,1,1137.29 +2017-11-16,amzn,le,1,1137.29 +2017-11-16,fb,sx,1,179.59 +2017-11-16,fb,le,1,179.59 +2017-11-16,aapl,sx,1,171.1 +2017-11-16,aapl,le,1,171.1 +2017-11-16,googl,sx,1,1048.47 +2017-11-16,googl,le,1,1048.47 +2017-11-17,nflx,lx,-1,193.2 +2017-11-17,nflx,se,-1,193.2 +2017-11-17,amzn,lx,-1,1129.88 +2017-11-17,amzn,se,-1,1129.88 +2017-11-17,fb,lx,-1,179.0 +2017-11-17,fb,se,-1,179.0 +2017-11-17,aapl,lx,-1,170.15 +2017-11-17,aapl,se,-1,170.15 +2017-11-17,googl,lx,-1,1035.89 +2017-11-17,googl,se,-1,1035.89 +2017-11-20,nflx,sx,1,194.1 +2017-11-20,nflx,le,1,194.1 +2017-11-21,amzn,sx,1,1139.49 +2017-11-21,amzn,le,1,1139.49 +2017-11-21,fb,sx,1,181.86 +2017-11-21,fb,le,1,181.86 +2017-11-21,aapl,sx,1,173.14 +2017-11-21,aapl,le,1,173.14 +2017-11-21,googl,sx,1,1050.3 +2017-11-21,googl,le,1,1050.3 +2017-11-22,fb,lx,-1,180.87 +2017-11-22,fb,se,-1,180.87 +2017-11-24,nflx,lx,-1,195.75 +2017-11-24,nflx,se,-1,195.75 +2017-11-24,fb,sx,1,182.78 +2017-11-24,fb,le,1,182.78 +2017-11-27,aapl,lx,-1,174.09 +2017-11-27,aapl,se,-1,174.09 +2017-11-28,nflx,sx,1,199.18 +2017-11-28,nflx,le,1,199.18 +2017-11-28,amzn,lx,-1,1193.6 +2017-11-28,amzn,se,-1,1193.6 +2017-11-28,fb,lx,-1,182.42 +2017-11-28,fb,se,-1,182.42 +2017-11-28,googl,lx,-1,1063.29 +2017-11-28,googl,se,-1,1063.29 +2017-11-29,nflx,lx,-1,188.15 +2017-11-29,nflx,se,-1,188.15 +2017-11-30,amzn,sx,1,1176.75 +2017-11-30,amzn,le,1,1176.75 +2017-11-30,fb,sx,1,177.18 +2017-11-30,fb,le,1,177.18 +2017-11-30,aapl,sx,1,171.85 +2017-11-30,aapl,le,1,171.85 +2017-12-01,amzn,lx,-1,1162.35 +2017-12-01,amzn,se,-1,1162.35 +2017-12-01,fb,lx,-1,175.1 +2017-12-01,fb,se,-1,175.1 +2017-12-01,aapl,lx,-1,171.05 +2017-12-01,aapl,se,-1,171.05 +2017-12-05,nflx,sx,1,184.21 +2017-12-05,nflx,le,1,184.21 +2017-12-05,amzn,sx,1,1141.57 +2017-12-05,amzn,le,1,1141.57 +2017-12-05,fb,sx,1,172.83 +2017-12-05,fb,le,1,172.83 +2017-12-05,googl,sx,1,1019.6 +2017-12-05,googl,le,1,1019.6 +2017-12-07,nflx,lx,-1,185.2 +2017-12-07,nflx,se,-1,185.2 +2017-12-07,aapl,sx,1,169.452 +2017-12-07,aapl,le,1,169.452 +2017-12-08,nflx,sx,1,188.54 +2017-12-08,nflx,le,1,188.54 +2017-12-08,fb,lx,-1,179.0 +2017-12-08,fb,se,-1,179.0 +2017-12-08,aapl,lx,-1,169.37 +2017-12-08,aapl,se,-1,169.37 +2017-12-11,nflx,lx,-1,186.22 +2017-12-11,nflx,se,-1,186.22 +2017-12-11,fb,sx,1,179.04 +2017-12-11,fb,le,1,179.04 +2017-12-11,aapl,sx,1,172.67 +2017-12-11,aapl,le,1,172.67 +2017-12-12,amzn,lx,-1,1165.08 +2017-12-12,amzn,se,-1,1165.08 +2017-12-12,fb,lx,-1,176.96 +2017-12-12,fb,se,-1,176.96 +2017-12-12,aapl,lx,-1,171.7 +2017-12-12,aapl,se,-1,171.7 +2017-12-12,googl,lx,-1,1048.77 +2017-12-12,googl,se,-1,1048.77 +2017-12-13,nflx,sx,1,187.86 +2017-12-13,nflx,le,1,187.86 +2017-12-13,fb,sx,1,178.3 +2017-12-13,fb,le,1,178.3 +2017-12-13,aapl,sx,1,172.27 +2017-12-13,aapl,le,1,172.27 +2017-12-13,googl,sx,1,1051.39 +2017-12-13,googl,le,1,1051.39 +2017-12-14,amzn,sx,1,1174.26 +2017-12-14,amzn,le,1,1174.26 +2017-12-14,aapl,lx,-1,172.22 +2017-12-14,aapl,se,-1,172.22 +2017-12-15,aapl,sx,1,173.87 +2017-12-15,aapl,le,1,173.87 +2017-12-19,nflx,lx,-1,187.02 +2017-12-19,nflx,se,-1,187.02 +2017-12-19,amzn,lx,-1,1187.38 +2017-12-19,amzn,se,-1,1187.38 +2017-12-19,fb,lx,-1,179.51 +2017-12-19,fb,se,-1,179.51 +2017-12-19,aapl,lx,-1,174.54 +2017-12-19,aapl,se,-1,174.54 +2017-12-19,googl,lx,-1,1079.78 +2017-12-19,googl,se,-1,1079.78 +2017-12-20,nflx,sx,1,188.82 +2017-12-20,nflx,le,1,188.82 +2017-12-21,nflx,lx,-1,188.62 +2017-12-21,nflx,se,-1,188.62 +2017-12-21,aapl,sx,1,175.01 +2017-12-21,aapl,le,1,175.01 +2017-12-22,nflx,sx,1,189.94 +2017-12-22,nflx,le,1,189.94 +2017-12-26,nflx,lx,-1,187.76 +2017-12-26,nflx,se,-1,187.76 +2017-12-26,amzn,sx,1,1176.76 +2017-12-26,amzn,le,1,1176.76 +2017-12-26,aapl,lx,-1,170.57 +2017-12-26,aapl,se,-1,170.57 +2017-12-27,fb,sx,1,177.62 +2017-12-27,fb,le,1,177.62 +2017-12-27,aapl,sx,1,170.6 +2017-12-27,aapl,le,1,170.6 +2017-12-28,nflx,sx,1,192.71 +2017-12-28,nflx,le,1,192.71 +2017-12-29,nflx,lx,-1,191.96 +2017-12-29,nflx,se,-1,191.96 +2017-12-29,amzn,lx,-1,1169.47 +2017-12-29,amzn,se,-1,1169.47 +2017-12-29,fb,lx,-1,176.46 +2017-12-29,fb,se,-1,176.46 +2017-12-29,aapl,lx,-1,169.23 +2017-12-29,aapl,se,-1,169.23 diff --git a/alphapy/examples/Trading System/systems/faang_closer_transactions_1d.csv b/alphapy/examples/Trading System/systems/faang_closer_transactions_1d.csv new file mode 100644 index 0000000..f501315 --- /dev/null +++ b/alphapy/examples/Trading System/systems/faang_closer_transactions_1d.csv @@ -0,0 +1,1748 @@ +date,amount,price,symbol +2016-08-19,-208.0,95.87,nflx +2016-08-19,-26.0,757.31,amzn +2016-08-19,-161.0,123.56,fb +2016-08-19,182.0,109.36,aapl +2016-08-19,-25.0,799.65,googl +2016-08-22,26.0,759.48,amzn +2016-08-22,26.0,759.48,amzn +2016-08-22,161.0,124.15,fb +2016-08-22,161.0,124.15,fb +2016-08-22,-182.0,108.51,aapl +2016-08-22,-184.0,108.51,aapl +2016-08-23,208.0,95.94,nflx +2016-08-23,208.0,95.94,nflx +2016-08-23,184.0,108.85,aapl +2016-08-23,183.0,108.85,aapl +2016-08-24,-208.0,95.18,nflx +2016-08-24,-210.0,95.18,nflx +2016-08-24,-26.0,757.25,amzn +2016-08-24,-26.0,757.25,amzn +2016-08-24,-161.0,123.48,fb +2016-08-24,-162.0,123.48,fb +2016-08-24,-183.0,108.03,aapl +2016-08-24,-185.0,108.03,aapl +2016-08-25,210.0,97.32,nflx +2016-08-25,204.0,97.32,nflx +2016-08-25,26.0,759.22,amzn +2016-08-25,26.0,759.22,amzn +2016-08-25,162.0,123.89,fb +2016-08-25,160.0,123.89,fb +2016-08-26,25.0,793.22,googl +2016-08-26,25.0,793.22,googl +2016-08-29,-204.0,97.3,nflx +2016-08-29,-206.0,97.3,nflx +2016-08-30,206.0,97.45,nflx +2016-08-30,206.0,97.45,nflx +2016-08-30,-26.0,767.58,amzn +2016-08-30,-26.0,767.58,amzn +2016-08-30,-160.0,125.84,fb +2016-08-30,-159.0,125.84,fb +2016-08-30,-25.0,791.92,googl +2016-08-30,-25.0,791.92,googl +2016-08-31,26.0,769.16,amzn +2016-08-31,26.0,769.16,amzn +2016-08-31,159.0,126.12,fb +2016-08-31,158.0,126.12,fb +2016-08-31,185.0,106.1,aapl +2016-08-31,188.0,106.1,aapl +2016-09-01,-206.0,97.38,nflx +2016-09-01,-205.0,97.38,nflx +2016-09-01,25.0,791.4,googl +2016-09-01,25.0,791.4,googl +2016-09-06,205.0,100.09,nflx +2016-09-06,201.0,100.09,nflx +2016-09-06,-188.0,107.7,aapl +2016-09-06,-187.0,107.7,aapl +2016-09-07,-201.0,99.15,nflx +2016-09-07,-206.0,99.15,nflx +2016-09-07,-26.0,784.48,amzn +2016-09-07,-26.0,784.48,amzn +2016-09-07,187.0,108.36,aapl +2016-09-07,189.0,108.36,aapl +2016-09-07,-25.0,807.99,googl +2016-09-07,-25.0,807.99,googl +2016-09-08,206.0,99.66,nflx +2016-09-08,205.0,99.66,nflx +2016-09-08,-158.0,130.27,fb +2016-09-08,-157.0,130.27,fb +2016-09-08,-189.0,105.52,aapl +2016-09-08,-194.0,105.52,aapl +2016-09-09,-205.0,96.5,nflx +2016-09-09,-211.0,96.5,nflx +2016-09-12,211.0,99.05,nflx +2016-09-12,200.0,99.05,nflx +2016-09-12,26.0,771.49,amzn +2016-09-12,25.0,771.49,amzn +2016-09-12,157.0,128.69,fb +2016-09-12,154.0,128.69,fb +2016-09-12,194.0,105.44,aapl +2016-09-12,188.0,105.44,aapl +2016-09-12,25.0,798.82,googl +2016-09-12,24.0,798.82,googl +2016-09-13,-200.0,96.09,nflx +2016-09-13,-210.0,96.09,nflx +2016-09-13,-25.0,761.01,amzn +2016-09-13,-26.0,761.01,amzn +2016-09-13,-154.0,127.21,fb +2016-09-13,-158.0,127.21,fb +2016-09-13,-24.0,788.72,googl +2016-09-13,-25.0,788.72,googl +2016-09-14,210.0,97.01,nflx +2016-09-14,206.0,97.01,nflx +2016-09-14,26.0,761.09,amzn +2016-09-14,26.0,761.09,amzn +2016-09-14,158.0,127.77,fb +2016-09-14,156.0,127.77,fb +2016-09-14,25.0,790.46,googl +2016-09-14,25.0,790.46,googl +2016-09-16,-188.0,114.92,aapl +2016-09-16,-178.0,114.92,aapl +2016-09-16,-25.0,797.97,googl +2016-09-16,-25.0,797.97,googl +2016-09-19,-206.0,98.06,nflx +2016-09-19,-210.0,98.06,nflx +2016-09-19,-26.0,775.1,amzn +2016-09-19,-26.0,775.1,amzn +2016-09-19,-156.0,128.65,fb +2016-09-19,-160.0,128.65,fb +2016-09-20,210.0,98.25,nflx +2016-09-20,208.0,98.25,nflx +2016-09-20,26.0,780.22,amzn +2016-09-20,26.0,780.22,amzn +2016-09-20,25.0,799.78,googl +2016-09-20,25.0,799.78,googl +2016-09-21,-208.0,94.88,nflx +2016-09-21,-216.0,94.88,nflx +2016-09-21,160.0,129.94,fb +2016-09-21,158.0,129.94,fb +2016-09-22,216.0,95.83,nflx +2016-09-22,214.0,95.83,nflx +2016-09-22,178.0,114.62,aapl +2016-09-22,179.0,114.62,aapl +2016-09-23,-158.0,127.96,fb +2016-09-23,-162.0,127.96,fb +2016-09-23,-179.0,112.71,aapl +2016-09-23,-184.0,112.71,aapl +2016-09-23,-25.0,814.96,googl +2016-09-23,-25.0,814.96,googl +2016-09-26,-214.0,94.56,nflx +2016-09-26,-218.0,94.56,nflx +2016-09-26,-26.0,799.16,amzn +2016-09-26,-25.0,799.16,amzn +2016-09-26,184.0,112.88,aapl +2016-09-26,182.0,112.88,aapl +2016-09-27,218.0,97.07,nflx +2016-09-27,210.0,97.07,nflx +2016-09-27,25.0,816.11,amzn +2016-09-27,25.0,816.11,amzn +2016-09-27,162.0,128.69,fb +2016-09-27,158.0,128.69,fb +2016-09-27,25.0,810.73,googl +2016-09-27,25.0,810.73,googl +2016-09-28,-25.0,810.06,googl +2016-09-28,-25.0,810.06,googl +2016-09-29,-210.0,96.67,nflx +2016-09-29,-215.0,96.67,nflx +2016-09-29,-158.0,128.09,fb +2016-09-29,-162.0,128.09,fb +2016-09-29,-182.0,112.18,aapl +2016-09-29,-185.0,112.18,aapl +2016-09-30,215.0,98.55,nflx +2016-09-30,209.0,98.55,nflx +2016-09-30,162.0,128.27,fb +2016-09-30,161.0,128.27,fb +2016-09-30,185.0,113.05,aapl +2016-09-30,183.0,113.05,aapl +2016-09-30,25.0,804.06,googl +2016-09-30,25.0,804.06,googl +2016-10-03,-25.0,836.74,amzn +2016-10-03,-24.0,836.74,amzn +2016-10-03,-183.0,112.52,aapl +2016-10-03,-185.0,112.52,aapl +2016-10-03,-25.0,800.38,googl +2016-10-03,-26.0,800.38,googl +2016-10-04,-209.0,102.34,nflx +2016-10-04,-205.0,102.34,nflx +2016-10-04,-161.0,128.19,fb +2016-10-04,-163.0,128.19,fb +2016-10-04,185.0,113.0,aapl +2016-10-04,185.0,113.0,aapl +2016-10-04,26.0,802.79,googl +2016-10-04,26.0,802.79,googl +2016-10-05,205.0,106.28,nflx +2016-10-05,197.0,106.28,nflx +2016-10-05,24.0,844.36,amzn +2016-10-05,24.0,844.36,amzn +2016-10-05,163.0,128.47,fb +2016-10-05,163.0,128.47,fb +2016-10-05,-26.0,801.23,googl +2016-10-05,-26.0,801.23,googl +2016-10-06,-197.0,105.07,nflx +2016-10-06,-201.0,105.07,nflx +2016-10-06,-24.0,841.66,amzn +2016-10-06,-25.0,841.66,amzn +2016-10-06,26.0,803.08,googl +2016-10-06,26.0,803.08,googl +2016-10-07,-26.0,800.71,googl +2016-10-07,-26.0,800.71,googl +2016-10-10,25.0,841.71,amzn +2016-10-10,25.0,841.71,amzn +2016-10-10,26.0,814.17,googl +2016-10-10,26.0,814.17,googl +2016-10-11,-25.0,831.0,amzn +2016-10-11,-25.0,831.0,amzn +2016-10-11,-163.0,128.88,fb +2016-10-11,-165.0,128.88,fb +2016-10-11,-26.0,809.57,googl +2016-10-11,-26.0,809.57,googl +2016-10-12,25.0,834.09,amzn +2016-10-12,25.0,834.09,amzn +2016-10-12,165.0,129.05,fb +2016-10-12,163.0,129.05,fb +2016-10-12,26.0,811.77,googl +2016-10-12,25.0,811.77,googl +2016-10-13,201.0,100.23,nflx +2016-10-13,210.0,100.23,nflx +2016-10-13,-25.0,829.28,amzn +2016-10-13,-25.0,829.28,amzn +2016-10-13,-163.0,127.82,fb +2016-10-13,-165.0,127.82,fb +2016-10-13,-185.0,116.98,aapl +2016-10-13,-180.0,116.98,aapl +2016-10-13,-25.0,804.08,googl +2016-10-13,-26.0,804.08,googl +2016-10-14,165.0,127.88,fb +2016-10-14,164.0,127.88,fb +2016-10-14,180.0,117.63,aapl +2016-10-14,178.0,117.63,aapl +2016-10-14,26.0,804.6,googl +2016-10-14,26.0,804.6,googl +2016-10-17,-210.0,99.8,nflx +2016-10-17,-211.0,99.8,nflx +2016-10-17,-164.0,127.54,fb +2016-10-17,-165.0,127.54,fb +2016-10-17,-178.0,117.55,aapl +2016-10-17,-179.0,117.55,aapl +2016-10-18,211.0,118.79,nflx +2016-10-18,176.0,118.79,nflx +2016-10-18,25.0,817.65,amzn +2016-10-18,25.0,817.65,amzn +2016-10-18,165.0,128.57,fb +2016-10-18,162.0,128.57,fb +2016-10-20,-25.0,810.32,amzn +2016-10-20,-27.0,810.32,amzn +2016-10-20,-162.0,130.0,fb +2016-10-20,-169.0,130.0,fb +2016-10-20,-26.0,821.63,googl +2016-10-20,-26.0,821.63,googl +2016-10-21,27.0,818.99,amzn +2016-10-21,26.0,818.99,amzn +2016-10-21,169.0,132.07,fb +2016-10-21,166.0,132.07,fb +2016-10-21,26.0,824.06,googl +2016-10-21,26.0,824.06,googl +2016-10-24,-176.0,127.33,nflx +2016-10-24,-175.0,127.33,nflx +2016-10-24,179.0,117.65,aapl +2016-10-24,189.0,117.65,aapl +2016-10-25,-26.0,835.18,amzn +2016-10-25,-26.0,835.18,amzn +2016-10-25,-166.0,132.29,fb +2016-10-25,-170.0,132.29,fb +2016-10-25,-26.0,828.55,googl +2016-10-25,-27.0,828.55,googl +2016-10-26,175.0,126.97,nflx +2016-10-26,176.0,126.97,nflx +2016-10-26,-189.0,115.59,aapl +2016-10-26,-194.0,115.59,aapl +2016-10-27,-176.0,126.47,nflx +2016-10-27,-175.0,126.47,nflx +2016-10-28,175.0,126.57,nflx +2016-10-28,174.0,126.57,nflx +2016-10-28,170.0,131.29,fb +2016-10-28,168.0,131.29,fb +2016-10-28,27.0,819.56,googl +2016-10-28,26.0,819.56,googl +2016-10-31,-174.0,124.87,nflx +2016-10-31,-175.0,124.87,nflx +2016-10-31,26.0,789.82,amzn +2016-10-31,27.0,789.82,amzn +2016-10-31,-168.0,130.99,fb +2016-10-31,-167.0,130.99,fb +2016-10-31,-26.0,809.9,googl +2016-10-31,-27.0,809.9,googl +2016-11-01,-27.0,785.41,amzn +2016-11-01,-27.0,785.41,amzn +2016-11-02,194.0,111.59,aapl +2016-11-02,193.0,111.59,aapl +2016-11-03,27.0,767.03,amzn +2016-11-03,27.0,767.03,amzn +2016-11-03,-193.0,109.83,aapl +2016-11-03,-193.0,109.83,aapl +2016-11-04,-27.0,755.05,amzn +2016-11-04,-27.0,755.05,amzn +2016-11-04,167.0,120.75,fb +2016-11-04,173.0,120.75,fb +2016-11-07,175.0,124.58,nflx +2016-11-07,167.0,124.58,nflx +2016-11-07,27.0,784.93,amzn +2016-11-07,26.0,784.93,amzn +2016-11-07,193.0,110.41,aapl +2016-11-07,188.0,110.41,aapl +2016-11-07,27.0,802.03,googl +2016-11-07,26.0,802.03,googl +2016-11-08,-167.0,124.34,nflx +2016-11-08,-171.0,124.34,nflx +2016-11-09,-26.0,771.88,amzn +2016-11-09,-27.0,771.88,amzn +2016-11-09,-173.0,123.18,fb +2016-11-09,-174.0,123.18,fb +2016-11-09,-188.0,110.88,aapl +2016-11-09,-193.0,110.88,aapl +2016-11-09,-26.0,805.59,googl +2016-11-09,-26.0,805.59,googl +2016-11-11,193.0,108.43,aapl +2016-11-11,189.0,108.43,aapl +2016-11-14,-189.0,105.71,aapl +2016-11-14,-193.0,105.71,aapl +2016-11-15,171.0,113.59,nflx +2016-11-15,175.0,113.59,nflx +2016-11-15,27.0,743.24,amzn +2016-11-15,26.0,743.24,amzn +2016-11-15,174.0,117.2,fb +2016-11-15,169.0,117.2,fb +2016-11-15,193.0,107.11,aapl +2016-11-15,185.0,107.11,aapl +2016-11-15,26.0,775.16,googl +2016-11-15,25.0,775.16,googl +2016-11-16,-169.0,116.34,fb +2016-11-16,-174.0,116.34,fb +2016-11-17,-175.0,115.03,nflx +2016-11-17,-177.0,115.03,nflx +2016-11-17,174.0,117.79,fb +2016-11-17,173.0,117.79,fb +2016-11-17,-185.0,109.95,aapl +2016-11-17,-186.0,109.95,aapl +2016-11-18,177.0,115.21,nflx +2016-11-18,178.0,115.21,nflx +2016-11-18,-173.0,117.02,fb +2016-11-18,-176.0,117.02,fb +2016-11-18,186.0,110.06,aapl +2016-11-18,187.0,110.06,aapl +2016-11-18,-25.0,775.97,googl +2016-11-18,-26.0,775.97,googl +2016-11-21,176.0,121.77,fb +2016-11-21,168.0,121.77,fb +2016-11-21,26.0,784.8,googl +2016-11-21,26.0,784.8,googl +2016-11-22,-168.0,121.47,fb +2016-11-22,-173.0,121.47,fb +2016-11-23,-178.0,117.69,nflx +2016-11-23,-178.0,117.69,nflx +2016-11-23,-26.0,780.12,amzn +2016-11-23,-26.0,780.12,amzn +2016-11-23,-187.0,111.23,aapl +2016-11-23,-189.0,111.23,aapl +2016-11-23,-26.0,779.0,googl +2016-11-23,-27.0,779.0,googl +2016-11-25,26.0,780.37,amzn +2016-11-25,26.0,780.37,amzn +2016-11-25,189.0,111.79,aapl +2016-11-25,187.0,111.79,aapl +2016-11-25,27.0,780.23,googl +2016-11-25,26.0,780.23,googl +2016-11-28,-26.0,766.77,amzn +2016-11-28,-27.0,766.77,amzn +2016-11-28,173.0,120.41,fb +2016-11-28,173.0,120.41,fb +2016-11-28,-187.0,111.57,aapl +2016-11-28,-187.0,111.57,aapl +2016-11-29,178.0,117.51,nflx +2016-11-29,177.0,117.51,nflx +2016-11-30,-177.0,117.0,nflx +2016-11-30,-178.0,117.0,nflx +2016-11-30,-173.0,118.42,fb +2016-11-30,-176.0,118.42,fb +2016-11-30,-26.0,775.88,googl +2016-11-30,-26.0,775.88,googl +2016-12-01,178.0,117.22,nflx +2016-12-01,175.0,117.22,nflx +2016-12-02,176.0,115.4,fb +2016-12-02,176.0,115.4,fb +2016-12-02,187.0,109.9,aapl +2016-12-02,185.0,109.9,aapl +2016-12-02,26.0,764.46,googl +2016-12-02,26.0,764.46,googl +2016-12-05,-175.0,119.16,nflx +2016-12-05,-172.0,119.16,nflx +2016-12-05,27.0,759.36,amzn +2016-12-05,27.0,759.36,amzn +2016-12-05,-185.0,109.11,aapl +2016-12-05,-188.0,109.11,aapl +2016-12-06,172.0,124.57,nflx +2016-12-06,165.0,124.57,nflx +2016-12-06,-176.0,117.31,fb +2016-12-06,-176.0,117.31,fb +2016-12-06,188.0,109.95,aapl +2016-12-06,188.0,109.95,aapl +2016-12-06,-26.0,776.18,googl +2016-12-06,-26.0,776.18,googl +2016-12-07,176.0,117.95,fb +2016-12-07,177.0,117.95,fb +2016-12-07,26.0,791.47,googl +2016-12-07,26.0,791.47,googl +2016-12-08,-165.0,123.24,nflx +2016-12-08,-171.0,123.24,nflx +2016-12-08,-27.0,767.33,amzn +2016-12-08,-27.0,767.33,amzn +2016-12-09,27.0,768.66,amzn +2016-12-09,27.0,768.66,amzn +2016-12-12,-27.0,760.12,amzn +2016-12-12,-27.0,760.12,amzn +2016-12-12,-177.0,117.77,fb +2016-12-12,-180.0,117.77,fb +2016-12-12,-188.0,113.3,aapl +2016-12-12,-187.0,113.3,aapl +2016-12-12,-26.0,807.9,googl +2016-12-12,-26.0,807.9,googl +2016-12-13,171.0,123.78,nflx +2016-12-13,170.0,123.78,nflx +2016-12-13,27.0,774.34,amzn +2016-12-13,27.0,774.34,amzn +2016-12-13,180.0,120.31,fb +2016-12-13,175.0,120.31,fb +2016-12-13,187.0,115.19,aapl +2016-12-13,183.0,115.19,aapl +2016-12-13,26.0,815.34,googl +2016-12-13,25.0,815.34,googl +2016-12-14,-170.0,123.44,nflx +2016-12-14,-173.0,123.44,nflx +2016-12-14,-27.0,768.82,amzn +2016-12-14,-27.0,768.82,amzn +2016-12-14,-175.0,120.21,fb +2016-12-14,-178.0,120.21,fb +2016-12-15,173.0,125.0,nflx +2016-12-15,171.0,125.0,nflx +2016-12-15,178.0,120.57,fb +2016-12-15,177.0,120.57,fb +2016-12-15,-25.0,815.65,googl +2016-12-15,-26.0,815.65,googl +2016-12-16,-171.0,124.22,nflx +2016-12-16,-172.0,124.22,nflx +2016-12-16,-177.0,119.87,fb +2016-12-16,-178.0,119.87,fb +2016-12-19,172.0,125.45,nflx +2016-12-19,170.0,125.45,nflx +2016-12-19,27.0,766.0,amzn +2016-12-19,27.0,766.0,amzn +2016-12-19,26.0,812.5,googl +2016-12-19,26.0,812.5,googl +2016-12-20,-170.0,125.12,nflx +2016-12-20,-171.0,125.12,nflx +2016-12-21,171.0,126.5,nflx +2016-12-21,169.0,126.5,nflx +2016-12-21,-27.0,770.6,amzn +2016-12-21,-27.0,770.6,amzn +2016-12-21,-26.0,812.2,googl +2016-12-21,-26.0,812.2,googl +2016-12-22,-169.0,125.58,nflx +2016-12-22,-171.0,125.58,nflx +2016-12-22,-183.0,116.29,aapl +2016-12-22,-185.0,116.29,aapl +2016-12-23,171.0,125.59,nflx +2016-12-23,170.0,125.59,nflx +2016-12-23,185.0,116.52,aapl +2016-12-23,183.0,116.52,aapl +2016-12-27,27.0,771.4,amzn +2016-12-27,27.0,771.4,amzn +2016-12-27,178.0,118.01,fb +2016-12-27,180.0,118.01,fb +2016-12-27,26.0,809.93,googl +2016-12-27,26.0,809.93,googl +2016-12-28,-170.0,125.89,nflx +2016-12-28,-171.0,125.89,nflx +2016-12-28,-180.0,116.92,fb +2016-12-28,-184.0,116.92,fb +2016-12-28,-183.0,116.76,aapl +2016-12-28,-184.0,116.76,aapl +2016-12-28,-26.0,804.57,googl +2016-12-28,-26.0,804.57,googl +2016-12-29,-27.0,765.15,amzn +2016-12-29,-27.0,765.15,amzn +2017-01-03,171.0,127.49,nflx +2017-01-03,164.0,127.49,nflx +2017-01-03,27.0,753.67,amzn +2017-01-03,27.0,753.67,amzn +2017-01-03,184.0,116.86,fb +2017-01-03,179.0,116.86,fb +2017-01-03,184.0,116.15,aapl +2017-01-03,181.0,116.15,aapl +2017-01-03,26.0,808.01,googl +2017-01-03,26.0,808.01,googl +2017-01-04,-181.0,116.02,aapl +2017-01-04,-183.0,116.02,aapl +2017-01-04,-26.0,807.77,googl +2017-01-04,-26.0,807.77,googl +2017-01-05,183.0,116.61,aapl +2017-01-05,184.0,116.61,aapl +2017-01-05,26.0,813.02,googl +2017-01-05,26.0,813.02,googl +2017-01-06,-164.0,131.07,nflx +2017-01-06,-166.0,131.07,nflx +2017-01-10,-27.0,795.9,amzn +2017-01-10,-27.0,795.9,amzn +2017-01-10,-179.0,124.35,fb +2017-01-10,-178.0,124.35,fb +2017-01-10,-26.0,826.01,googl +2017-01-10,-26.0,826.01,googl +2017-01-11,166.0,130.5,nflx +2017-01-11,169.0,130.5,nflx +2017-01-11,27.0,799.02,amzn +2017-01-11,27.0,799.02,amzn +2017-01-11,178.0,126.09,fb +2017-01-11,175.0,126.09,fb +2017-01-11,26.0,829.86,googl +2017-01-11,26.0,829.86,googl +2017-01-12,-169.0,129.18,nflx +2017-01-12,-172.0,129.18,nflx +2017-01-12,-184.0,119.25,aapl +2017-01-12,-186.0,119.25,aapl +2017-01-12,-26.0,829.53,googl +2017-01-12,-26.0,829.53,googl +2017-01-13,172.0,133.7,nflx +2017-01-13,166.0,133.7,nflx +2017-01-13,26.0,830.94,googl +2017-01-13,26.0,830.94,googl +2017-01-17,-166.0,132.89,nflx +2017-01-17,-169.0,132.89,nflx +2017-01-17,-27.0,809.72,amzn +2017-01-17,-27.0,809.72,amzn +2017-01-17,-175.0,127.87,fb +2017-01-17,-176.0,127.87,fb +2017-01-17,186.0,120.0,aapl +2017-01-17,187.0,120.0,aapl +2017-01-17,-26.0,827.46,googl +2017-01-17,-27.0,827.46,googl +2017-01-18,169.0,133.26,nflx +2017-01-18,168.0,133.26,nflx +2017-01-18,176.0,127.92,fb +2017-01-18,175.0,127.92,fb +2017-01-18,-187.0,119.99,aapl +2017-01-18,-187.0,119.99,aapl +2017-01-18,27.0,829.02,googl +2017-01-18,27.0,829.02,googl +2017-01-19,27.0,809.04,amzn +2017-01-19,27.0,809.04,amzn +2017-01-19,-175.0,127.55,fb +2017-01-19,-176.0,127.55,fb +2017-01-19,-27.0,824.37,googl +2017-01-19,-27.0,824.37,googl +2017-01-20,-27.0,808.33,amzn +2017-01-20,-27.0,808.33,amzn +2017-01-20,187.0,120.0,aapl +2017-01-20,188.0,120.0,aapl +2017-01-20,27.0,828.17,googl +2017-01-20,27.0,828.17,googl +2017-01-23,-168.0,137.39,nflx +2017-01-23,-164.0,137.39,nflx +2017-01-23,27.0,817.88,amzn +2017-01-23,27.0,817.88,amzn +2017-01-23,176.0,128.93,fb +2017-01-23,175.0,128.93,fb +2017-01-24,164.0,140.11,nflx +2017-01-24,162.0,140.11,nflx +2017-01-24,-188.0,119.97,aapl +2017-01-24,-189.0,119.97,aapl +2017-01-25,-162.0,139.52,nflx +2017-01-25,-164.0,139.52,nflx +2017-01-25,189.0,121.88,aapl +2017-01-25,188.0,121.88,aapl +2017-01-26,-27.0,856.98,googl +2017-01-26,-27.0,856.98,googl +2017-01-27,164.0,142.45,nflx +2017-01-27,163.0,142.45,nflx +2017-01-27,-27.0,835.77,amzn +2017-01-27,-27.0,835.77,amzn +2017-01-27,-175.0,132.18,fb +2017-01-27,-175.0,132.18,fb +2017-01-30,-163.0,141.22,nflx +2017-01-30,-164.0,141.22,nflx +2017-01-30,-188.0,121.63,aapl +2017-01-30,-191.0,121.63,aapl +2017-02-01,164.0,140.78,nflx +2017-02-01,162.0,140.78,nflx +2017-02-01,27.0,832.35,amzn +2017-02-01,27.0,832.35,amzn +2017-02-01,175.0,133.23,fb +2017-02-01,171.0,133.23,fb +2017-02-01,191.0,128.75,aapl +2017-02-01,177.0,128.75,aapl +2017-02-02,-162.0,139.2,nflx +2017-02-02,-167.0,139.2,nflx +2017-02-02,-171.0,130.84,fb +2017-02-02,-178.0,130.84,fb +2017-02-02,-177.0,128.53,aapl +2017-02-02,-181.0,128.53,aapl +2017-02-02,27.0,818.26,googl +2017-02-02,28.0,818.26,googl +2017-02-03,167.0,140.25,nflx +2017-02-03,165.0,140.25,nflx +2017-02-03,-27.0,810.2,amzn +2017-02-03,-28.0,810.2,amzn +2017-02-03,178.0,130.98,fb +2017-02-03,177.0,130.98,fb +2017-02-03,181.0,129.08,aapl +2017-02-03,179.0,129.08,aapl +2017-02-07,28.0,812.5,amzn +2017-02-07,28.0,812.5,amzn +2017-02-07,-177.0,131.84,fb +2017-02-07,-176.0,131.84,fb +2017-02-08,176.0,134.2,fb +2017-02-08,174.0,134.2,fb +2017-02-09,-165.0,144.14,nflx +2017-02-09,-163.0,144.14,nflx +2017-02-09,-174.0,134.14,fb +2017-02-09,-175.0,134.14,fb +2017-02-10,163.0,144.82,nflx +2017-02-10,162.0,144.82,nflx +2017-02-10,175.0,134.19,fb +2017-02-10,175.0,134.19,fb +2017-02-10,-179.0,132.12,aapl +2017-02-10,-178.0,132.12,aapl +2017-02-13,-162.0,143.2,nflx +2017-02-13,-165.0,143.2,nflx +2017-02-13,-175.0,134.05,fb +2017-02-13,-176.0,134.05,fb +2017-02-13,178.0,133.29,aapl +2017-02-13,177.0,133.29,aapl +2017-02-14,-28.0,836.39,amzn +2017-02-14,-28.0,836.39,amzn +2017-02-15,165.0,142.27,nflx +2017-02-15,166.0,142.27,nflx +2017-02-15,28.0,842.7,amzn +2017-02-15,28.0,842.7,amzn +2017-02-15,-28.0,837.32,googl +2017-02-15,-28.0,837.32,googl +2017-02-16,-166.0,142.01,nflx +2017-02-16,-167.0,142.01,nflx +2017-02-16,176.0,133.84,fb +2017-02-16,177.0,133.84,fb +2017-02-16,-177.0,135.345,aapl +2017-02-16,-175.0,135.345,aapl +2017-02-16,28.0,842.17,googl +2017-02-16,28.0,842.17,googl +2017-02-17,167.0,142.22,nflx +2017-02-17,167.0,142.22,nflx +2017-02-17,-177.0,133.53,fb +2017-02-17,-178.0,133.53,fb +2017-02-17,175.0,135.72,aapl +2017-02-17,175.0,135.72,aapl +2017-02-21,178.0,133.72,fb +2017-02-21,178.0,133.72,fb +2017-02-22,-28.0,855.61,amzn +2017-02-22,-28.0,855.61,amzn +2017-02-23,-167.0,142.78,nflx +2017-02-23,-169.0,142.78,nflx +2017-02-23,-178.0,135.36,fb +2017-02-23,-178.0,135.36,fb +2017-02-23,-175.0,136.53,aapl +2017-02-23,-176.0,136.53,aapl +2017-02-23,-28.0,851.0,googl +2017-02-23,-28.0,851.0,googl +2017-02-24,169.0,143.25,nflx +2017-02-24,167.0,143.25,nflx +2017-02-24,178.0,135.44,fb +2017-02-24,177.0,135.44,fb +2017-02-24,176.0,136.66,aapl +2017-02-24,175.0,136.66,aapl +2017-02-27,28.0,848.64,amzn +2017-02-27,28.0,848.64,amzn +2017-02-27,28.0,849.67,googl +2017-02-27,28.0,849.67,googl +2017-02-28,-167.0,142.13,nflx +2017-02-28,-169.0,142.13,nflx +2017-02-28,-28.0,845.04,amzn +2017-02-28,-28.0,845.04,amzn +2017-02-28,-177.0,135.54,fb +2017-02-28,-177.0,135.54,fb +2017-02-28,-28.0,844.93,googl +2017-02-28,-28.0,844.93,googl +2017-03-01,169.0,142.65,nflx +2017-03-01,167.0,142.65,nflx +2017-03-01,28.0,853.08,amzn +2017-03-01,28.0,853.08,amzn +2017-03-01,177.0,137.42,fb +2017-03-01,174.0,137.42,fb +2017-03-01,28.0,856.75,googl +2017-03-01,27.0,856.75,googl +2017-03-02,-167.0,139.53,nflx +2017-03-02,-173.0,139.53,nflx +2017-03-02,-28.0,848.91,amzn +2017-03-02,-28.0,848.91,amzn +2017-03-02,-174.0,136.76,fb +2017-03-02,-177.0,136.76,fb +2017-03-02,-175.0,138.96,aapl +2017-03-02,-174.0,138.96,aapl +2017-03-02,-27.0,849.85,googl +2017-03-02,-28.0,849.85,googl +2017-03-03,28.0,849.88,amzn +2017-03-03,28.0,849.88,amzn +2017-03-03,177.0,137.17,fb +2017-03-03,175.0,137.17,fb +2017-03-03,174.0,139.78,aapl +2017-03-03,171.0,139.78,aapl +2017-03-06,173.0,141.94,nflx +2017-03-06,169.0,141.94,nflx +2017-03-06,-28.0,846.61,amzn +2017-03-06,-28.0,846.61,amzn +2017-03-06,-171.0,139.34,aapl +2017-03-06,-172.0,139.34,aapl +2017-03-07,-169.0,141.43,nflx +2017-03-07,-170.0,141.43,nflx +2017-03-07,-175.0,137.3,fb +2017-03-07,-175.0,137.3,fb +2017-03-07,172.0,139.52,aapl +2017-03-07,172.0,139.52,aapl +2017-03-07,28.0,851.15,googl +2017-03-07,28.0,851.15,googl +2017-03-08,28.0,850.5,amzn +2017-03-08,28.0,850.5,amzn +2017-03-08,175.0,137.72,fb +2017-03-08,175.0,137.72,fb +2017-03-08,-172.0,139.0,aapl +2017-03-08,-173.0,139.0,aapl +2017-03-09,170.0,140.53,nflx +2017-03-09,171.0,140.53,nflx +2017-03-10,-28.0,852.46,amzn +2017-03-10,-28.0,852.46,amzn +2017-03-10,173.0,139.14,aapl +2017-03-10,173.0,139.14,aapl +2017-03-13,28.0,854.59,amzn +2017-03-13,28.0,854.59,amzn +2017-03-14,-171.0,143.19,nflx +2017-03-14,-170.0,143.19,nflx +2017-03-14,-28.0,852.53,amzn +2017-03-14,-28.0,852.53,amzn +2017-03-14,-175.0,139.32,fb +2017-03-14,-175.0,139.32,fb +2017-03-14,-173.0,138.99,aapl +2017-03-14,-175.0,138.99,aapl +2017-03-15,170.0,145.25,nflx +2017-03-15,167.0,145.25,nflx +2017-03-15,28.0,852.97,amzn +2017-03-15,28.0,852.97,amzn +2017-03-15,175.0,139.72,fb +2017-03-15,174.0,139.72,fb +2017-03-15,175.0,140.46,aapl +2017-03-15,173.0,140.46,aapl +2017-03-16,-167.0,144.39,nflx +2017-03-16,-169.0,144.39,nflx +2017-03-17,169.0,145.11,nflx +2017-03-17,168.0,145.11,nflx +2017-03-17,-28.0,852.31,amzn +2017-03-17,-28.0,852.31,amzn +2017-03-17,-174.0,139.84,fb +2017-03-17,-175.0,139.84,fb +2017-03-17,-173.0,139.99,aapl +2017-03-17,-175.0,139.99,aapl +2017-03-20,28.0,856.97,amzn +2017-03-20,28.0,856.97,amzn +2017-03-20,175.0,139.94,fb +2017-03-20,175.0,139.94,fb +2017-03-20,175.0,141.46,aapl +2017-03-20,173.0,141.46,aapl +2017-03-20,-28.0,867.91,googl +2017-03-20,-28.0,867.91,googl +2017-03-21,-168.0,142.42,nflx +2017-03-21,-172.0,142.42,nflx +2017-03-21,-28.0,843.2,amzn +2017-03-21,-29.0,843.2,amzn +2017-03-21,-175.0,138.51,fb +2017-03-21,-177.0,138.51,fb +2017-03-21,-173.0,139.84,aapl +2017-03-21,-175.0,139.84,aapl +2017-03-22,172.0,142.65,nflx +2017-03-22,169.0,142.65,nflx +2017-03-22,29.0,848.06,amzn +2017-03-22,28.0,848.06,amzn +2017-03-22,177.0,139.59,fb +2017-03-22,173.0,139.59,fb +2017-03-22,175.0,141.42,aapl +2017-03-22,171.0,141.42,aapl +2017-03-23,-169.0,141.84,nflx +2017-03-23,-171.0,141.84,nflx +2017-03-23,-28.0,847.38,amzn +2017-03-23,-28.0,847.38,amzn +2017-03-23,-173.0,139.53,fb +2017-03-23,-174.0,139.53,fb +2017-03-23,-171.0,140.92,aapl +2017-03-23,-172.0,140.92,aapl +2017-03-24,171.0,142.02,nflx +2017-03-24,170.0,142.02,nflx +2017-03-24,174.0,140.34,fb +2017-03-24,172.0,140.34,fb +2017-03-27,28.0,846.82,amzn +2017-03-27,28.0,846.82,amzn +2017-03-27,-172.0,140.32,fb +2017-03-27,-172.0,140.32,fb +2017-03-27,172.0,140.88,aapl +2017-03-27,171.0,140.88,aapl +2017-03-27,28.0,838.51,googl +2017-03-27,28.0,838.51,googl +2017-03-28,172.0,141.76,fb +2017-03-28,171.0,141.76,fb +2017-03-30,-171.0,142.41,fb +2017-03-30,-174.0,142.41,fb +2017-03-30,-171.0,143.93,aapl +2017-03-30,-172.0,143.93,aapl +2017-03-30,-28.0,849.48,googl +2017-03-30,-29.0,849.48,googl +2017-03-31,-170.0,147.81,nflx +2017-03-31,-168.0,147.81,nflx +2017-04-03,174.0,142.28,fb +2017-04-03,174.0,142.28,fb +2017-04-03,172.0,143.7,aapl +2017-04-03,173.0,143.7,aapl +2017-04-03,29.0,856.75,googl +2017-04-03,29.0,856.75,googl +2017-04-04,-174.0,141.73,fb +2017-04-04,-175.0,141.73,fb +2017-04-04,-29.0,852.57,googl +2017-04-04,-29.0,852.57,googl +2017-04-05,175.0,141.85,fb +2017-04-05,175.0,141.85,fb +2017-04-05,-173.0,144.02,aapl +2017-04-05,-173.0,144.02,aapl +2017-04-06,168.0,143.74,nflx +2017-04-06,173.0,143.74,nflx +2017-04-06,-28.0,898.28,amzn +2017-04-06,-27.0,898.28,amzn +2017-04-06,-175.0,141.17,fb +2017-04-06,-176.0,141.17,fb +2017-04-07,-173.0,143.11,nflx +2017-04-07,-172.0,143.11,nflx +2017-04-10,172.0,143.85,nflx +2017-04-10,171.0,143.85,nflx +2017-04-10,27.0,907.04,amzn +2017-04-10,27.0,907.04,amzn +2017-04-10,176.0,141.04,fb +2017-04-10,174.0,141.04,fb +2017-04-11,-27.0,902.36,amzn +2017-04-11,-27.0,902.36,amzn +2017-04-11,-174.0,139.92,fb +2017-04-11,-176.0,139.92,fb +2017-04-12,-171.0,143.83,nflx +2017-04-12,-171.0,143.83,nflx +2017-04-12,173.0,141.8,aapl +2017-04-12,173.0,141.8,aapl +2017-04-12,29.0,841.46,googl +2017-04-12,29.0,841.46,googl +2017-04-13,-173.0,141.05,aapl +2017-04-13,-174.0,141.05,aapl +2017-04-13,-29.0,840.18,googl +2017-04-13,-29.0,840.18,googl +2017-04-17,171.0,147.25,nflx +2017-04-17,166.0,147.25,nflx +2017-04-17,27.0,901.99,amzn +2017-04-17,27.0,901.99,amzn +2017-04-17,176.0,141.42,fb +2017-04-17,173.0,141.42,fb +2017-04-17,174.0,141.83,aapl +2017-04-17,172.0,141.83,aapl +2017-04-17,29.0,855.13,googl +2017-04-17,28.0,855.13,googl +2017-04-18,-166.0,143.36,nflx +2017-04-18,-173.0,143.36,nflx +2017-04-18,-173.0,140.96,fb +2017-04-18,-176.0,140.96,fb +2017-04-18,-172.0,141.2,aapl +2017-04-18,-176.0,141.2,aapl +2017-04-18,-28.0,853.99,googl +2017-04-18,-29.0,853.99,googl +2017-04-19,-27.0,899.2,amzn +2017-04-19,-27.0,899.2,amzn +2017-04-19,176.0,142.27,fb +2017-04-19,173.0,142.27,fb +2017-04-19,29.0,856.51,googl +2017-04-19,28.0,856.51,googl +2017-04-20,173.0,141.18,nflx +2017-04-20,174.0,141.18,nflx +2017-04-20,27.0,902.06,amzn +2017-04-20,27.0,902.06,amzn +2017-04-20,176.0,142.44,aapl +2017-04-20,172.0,142.44,aapl +2017-04-21,-27.0,898.53,amzn +2017-04-21,-27.0,898.53,amzn +2017-04-21,-173.0,143.68,fb +2017-04-21,-172.0,143.68,fb +2017-04-21,-172.0,142.27,aapl +2017-04-21,-174.0,142.27,aapl +2017-04-21,-28.0,858.95,googl +2017-04-21,-28.0,858.95,googl +2017-04-24,27.0,907.41,amzn +2017-04-24,27.0,907.41,amzn +2017-04-24,172.0,145.47,fb +2017-04-24,170.0,145.47,fb +2017-04-24,174.0,143.64,aapl +2017-04-24,173.0,143.64,aapl +2017-04-24,28.0,878.93,googl +2017-04-24,28.0,878.93,googl +2017-04-26,-174.0,150.17,nflx +2017-04-26,-170.0,150.17,nflx +2017-04-26,-173.0,143.6508,aapl +2017-04-26,-177.0,143.6508,aapl +2017-04-27,170.0,153.08,nflx +2017-04-27,166.0,153.08,nflx +2017-04-27,177.0,143.79,aapl +2017-04-27,177.0,143.79,aapl +2017-04-28,-166.0,152.2,nflx +2017-04-28,-168.0,152.2,nflx +2017-04-28,-177.0,143.65,aapl +2017-04-28,-178.0,143.65,aapl +2017-05-01,168.0,155.35,nflx +2017-05-01,167.0,155.35,nflx +2017-05-01,178.0,146.6,aapl +2017-05-01,177.0,146.6,aapl +2017-05-02,-27.0,946.68,amzn +2017-05-02,-27.0,946.68,amzn +2017-05-03,-167.0,155.59,nflx +2017-05-03,-170.0,155.59,nflx +2017-05-03,-170.0,151.8,fb +2017-05-03,-174.0,151.8,fb +2017-05-03,-177.0,147.06,aapl +2017-05-03,-180.0,147.06,aapl +2017-05-04,170.0,157.25,nflx +2017-05-04,168.0,157.25,nflx +2017-05-05,-168.0,156.6,nflx +2017-05-05,-169.0,156.6,nflx +2017-05-05,180.0,148.96,aapl +2017-05-05,177.0,148.96,aapl +2017-05-05,-28.0,950.28,googl +2017-05-05,-27.0,950.28,googl +2017-05-08,27.0,949.04,amzn +2017-05-08,27.0,949.04,amzn +2017-05-08,174.0,151.06,fb +2017-05-08,175.0,151.06,fb +2017-05-08,27.0,958.69,googl +2017-05-08,27.0,958.69,googl +2017-05-09,169.0,157.46,nflx +2017-05-09,170.0,157.46,nflx +2017-05-09,-175.0,150.48,fb +2017-05-09,-177.0,150.48,fb +2017-05-09,-27.0,956.71,googl +2017-05-09,-27.0,956.71,googl +2017-05-10,-27.0,948.95,amzn +2017-05-10,-28.0,948.95,amzn +2017-05-10,-177.0,153.26,aapl +2017-05-10,-175.0,153.26,aapl +2017-05-11,-170.0,158.54,nflx +2017-05-11,-169.0,158.54,nflx +2017-05-11,175.0,153.95,aapl +2017-05-11,174.0,153.95,aapl +2017-05-11,27.0,955.89,googl +2017-05-11,28.0,955.89,googl +2017-05-12,169.0,160.81,nflx +2017-05-12,166.0,160.81,nflx +2017-05-12,28.0,961.35,amzn +2017-05-12,27.0,961.35,amzn +2017-05-12,177.0,150.33,fb +2017-05-12,178.0,150.33,fb +2017-05-12,-28.0,955.14,googl +2017-05-12,-28.0,955.14,googl +2017-05-15,-166.0,160.02,nflx +2017-05-15,-169.0,160.02,nflx +2017-05-15,-27.0,957.97,amzn +2017-05-15,-28.0,957.97,amzn +2017-05-15,-178.0,150.19,fb +2017-05-15,-180.0,150.19,fb +2017-05-15,-174.0,155.7,aapl +2017-05-15,-173.0,155.7,aapl +2017-05-15,28.0,959.22,googl +2017-05-15,28.0,959.22,googl +2017-05-16,28.0,966.07,amzn +2017-05-16,27.0,966.07,amzn +2017-05-17,-27.0,944.52,amzn +2017-05-17,-28.0,944.52,amzn +2017-05-17,-28.0,942.17,googl +2017-05-17,-28.0,942.17,googl +2017-05-18,169.0,155.7,nflx +2017-05-18,168.0,155.7,nflx +2017-05-18,28.0,958.49,amzn +2017-05-18,27.0,958.49,amzn +2017-05-18,180.0,147.66,fb +2017-05-18,177.0,147.66,fb +2017-05-18,173.0,152.54,aapl +2017-05-18,172.0,152.54,aapl +2017-05-18,28.0,950.5,googl +2017-05-18,27.0,950.5,googl +2017-05-23,-177.0,148.07,fb +2017-05-23,-181.0,148.07,fb +2017-05-23,-172.0,153.8,aapl +2017-05-23,-174.0,153.8,aapl +2017-05-24,-168.0,157.75,nflx +2017-05-24,-170.0,157.75,nflx +2017-05-24,181.0,150.04,fb +2017-05-24,179.0,150.04,fb +2017-05-25,170.0,163.05,nflx +2017-05-25,166.0,163.05,nflx +2017-05-25,174.0,153.87,aapl +2017-05-25,175.0,153.87,aapl +2017-05-26,-166.0,162.43,nflx +2017-05-26,-169.0,162.43,nflx +2017-05-26,-175.0,153.61,aapl +2017-05-26,-178.0,153.61,aapl +2017-05-30,169.0,163.22,nflx +2017-05-30,168.0,163.22,nflx +2017-05-30,178.0,153.67,aapl +2017-05-30,178.0,153.67,aapl +2017-05-31,-168.0,163.07,nflx +2017-05-31,-168.0,163.07,nflx +2017-05-31,-27.0,994.62,amzn +2017-05-31,-27.0,994.62,amzn +2017-05-31,-179.0,151.46,fb +2017-05-31,-181.0,151.46,fb +2017-05-31,-178.0,152.76,aapl +2017-05-31,-180.0,152.76,aapl +2017-05-31,-27.0,987.09,googl +2017-05-31,-27.0,987.09,googl +2017-06-01,27.0,995.95,amzn +2017-06-01,27.0,995.95,amzn +2017-06-01,181.0,151.53,fb +2017-06-01,180.0,151.53,fb +2017-06-01,180.0,153.18,aapl +2017-06-01,178.0,153.18,aapl +2017-06-01,27.0,988.29,googl +2017-06-01,27.0,988.29,googl +2017-06-02,168.0,165.18,nflx +2017-06-02,166.0,165.18,nflx +2017-06-05,-166.0,165.06,nflx +2017-06-05,-168.0,165.06,nflx +2017-06-05,-178.0,153.93,aapl +2017-06-05,-180.0,153.93,aapl +2017-06-06,168.0,165.17,nflx +2017-06-06,168.0,165.17,nflx +2017-06-06,-27.0,1002.97,amzn +2017-06-06,-27.0,1002.97,amzn +2017-06-06,-180.0,152.81,fb +2017-06-06,-181.0,152.81,fb +2017-06-06,180.0,154.45,aapl +2017-06-06,179.0,154.45,aapl +2017-06-06,-27.0,996.68,googl +2017-06-06,-27.0,996.68,googl +2017-06-07,27.0,1010.07,amzn +2017-06-07,27.0,1010.07,amzn +2017-06-07,181.0,153.12,fb +2017-06-07,180.0,153.12,fb +2017-06-07,27.0,1001.59,googl +2017-06-07,27.0,1001.59,googl +2017-06-08,-27.0,1009.94,amzn +2017-06-08,-27.0,1009.94,amzn +2017-06-08,-179.0,154.99,aapl +2017-06-08,-179.0,154.99,aapl +2017-06-09,-168.0,158.03,nflx +2017-06-09,-176.0,158.03,nflx +2017-06-09,-180.0,149.63,fb +2017-06-09,-186.0,149.63,fb +2017-06-09,-27.0,970.12,googl +2017-06-09,-28.0,970.12,googl +2017-06-13,176.0,152.72,nflx +2017-06-13,172.0,152.72,nflx +2017-06-13,27.0,980.79,amzn +2017-06-13,26.0,980.79,amzn +2017-06-13,186.0,150.68,fb +2017-06-13,174.0,150.68,fb +2017-06-13,179.0,146.59,aapl +2017-06-13,179.0,146.59,aapl +2017-06-13,28.0,970.5,googl +2017-06-13,27.0,970.5,googl +2017-06-14,-172.0,152.2,nflx +2017-06-14,-175.0,152.2,nflx +2017-06-14,-26.0,976.47,amzn +2017-06-14,-27.0,976.47,amzn +2017-06-14,-174.0,150.25,fb +2017-06-14,-177.0,150.25,fb +2017-06-14,-179.0,145.16,aapl +2017-06-14,-183.0,145.16,aapl +2017-06-14,-27.0,967.93,googl +2017-06-14,-27.0,967.93,googl +2017-06-16,175.0,152.38,nflx +2017-06-16,172.0,152.38,nflx +2017-06-16,27.0,987.71,amzn +2017-06-16,26.0,987.71,amzn +2017-06-16,177.0,150.64,fb +2017-06-16,174.0,150.64,fb +2017-06-19,183.0,146.34,aapl +2017-06-19,180.0,146.34,aapl +2017-06-19,27.0,975.22,googl +2017-06-19,27.0,975.22,googl +2017-06-20,-172.0,152.05,nflx +2017-06-20,-176.0,152.05,nflx +2017-06-20,-26.0,992.59,amzn +2017-06-20,-27.0,992.59,amzn +2017-06-20,-174.0,152.25,fb +2017-06-20,-176.0,152.25,fb +2017-06-20,-180.0,145.01,aapl +2017-06-20,-185.0,145.01,aapl +2017-06-20,-27.0,968.99,googl +2017-06-20,-27.0,968.99,googl +2017-06-21,176.0,155.03,nflx +2017-06-21,172.0,155.03,nflx +2017-06-21,27.0,1002.23,amzn +2017-06-21,26.0,1002.23,amzn +2017-06-21,176.0,153.91,fb +2017-06-21,173.0,153.91,fb +2017-06-21,185.0,145.87,aapl +2017-06-21,182.0,145.87,aapl +2017-06-21,27.0,978.59,googl +2017-06-21,27.0,978.59,googl +2017-06-22,-172.0,154.89,nflx +2017-06-22,-174.0,154.89,nflx +2017-06-22,-26.0,1001.3,amzn +2017-06-22,-26.0,1001.3,amzn +2017-06-22,-173.0,153.4,fb +2017-06-22,-175.0,153.4,fb +2017-06-22,-182.0,145.63,aapl +2017-06-22,-185.0,145.63,aapl +2017-06-22,-27.0,976.62,googl +2017-06-22,-27.0,976.62,googl +2017-06-23,174.0,158.03,nflx +2017-06-23,170.0,158.03,nflx +2017-06-23,26.0,1003.74,amzn +2017-06-23,26.0,1003.74,amzn +2017-06-23,175.0,155.07,fb +2017-06-23,173.0,155.07,fb +2017-06-23,185.0,146.35,aapl +2017-06-23,184.0,146.35,aapl +2017-06-23,27.0,986.09,googl +2017-06-23,27.0,986.09,googl +2017-06-26,-170.0,157.5,nflx +2017-06-26,-172.0,157.5,nflx +2017-06-26,-26.0,993.98,amzn +2017-06-26,-27.0,993.98,amzn +2017-06-26,-173.0,153.59,fb +2017-06-26,-177.0,153.59,fb +2017-06-26,-184.0,145.82,aapl +2017-06-26,-186.0,145.82,aapl +2017-06-26,-27.0,972.09,googl +2017-06-26,-27.0,972.09,googl +2017-06-28,172.0,153.41,nflx +2017-06-28,171.0,153.41,nflx +2017-06-28,27.0,990.33,amzn +2017-06-28,26.0,990.33,amzn +2017-06-28,177.0,153.24,fb +2017-06-28,171.0,153.24,fb +2017-06-28,186.0,145.83,aapl +2017-06-28,180.0,145.83,aapl +2017-06-28,27.0,961.01,googl +2017-06-28,27.0,961.01,googl +2017-06-29,-171.0,150.09,nflx +2017-06-29,-178.0,150.09,nflx +2017-06-29,-26.0,975.93,amzn +2017-06-29,-27.0,975.93,amzn +2017-06-29,-171.0,151.04,fb +2017-06-29,-177.0,151.04,fb +2017-06-29,-180.0,143.68,aapl +2017-06-29,-186.0,143.68,aapl +2017-06-29,-27.0,937.82,googl +2017-06-29,-28.0,937.82,googl +2017-06-30,186.0,144.02,aapl +2017-06-30,182.0,144.02,aapl +2017-07-03,-182.0,143.5,aapl +2017-07-03,-182.0,143.5,aapl +2017-07-05,178.0,147.61,nflx +2017-07-05,174.0,147.61,nflx +2017-07-05,27.0,971.4,amzn +2017-07-05,26.0,971.4,amzn +2017-07-05,177.0,150.34,fb +2017-07-05,171.0,150.34,fb +2017-07-05,182.0,144.09,aapl +2017-07-05,179.0,144.09,aapl +2017-07-05,28.0,932.26,googl +2017-07-05,27.0,932.26,googl +2017-07-06,-174.0,146.25,nflx +2017-07-06,-178.0,146.25,nflx +2017-07-06,-26.0,965.14,amzn +2017-07-06,-27.0,965.14,amzn +2017-07-06,-171.0,148.82,fb +2017-07-06,-175.0,148.82,fb +2017-07-06,-179.0,142.73,aapl +2017-07-06,-183.0,142.73,aapl +2017-07-06,-27.0,927.69,googl +2017-07-06,-28.0,927.69,googl +2017-07-07,178.0,150.18,nflx +2017-07-07,172.0,150.18,nflx +2017-07-07,27.0,978.76,amzn +2017-07-07,26.0,978.76,amzn +2017-07-07,175.0,151.44,fb +2017-07-07,171.0,151.44,fb +2017-07-07,183.0,144.18,aapl +2017-07-07,179.0,144.18,aapl +2017-07-07,28.0,940.81,googl +2017-07-07,27.0,940.81,googl +2017-07-11,-26.0,994.13,amzn +2017-07-11,-26.0,994.13,amzn +2017-07-12,26.0,1006.51,amzn +2017-07-12,26.0,1006.51,amzn +2017-07-13,-172.0,158.17,nflx +2017-07-13,-172.0,158.17,nflx +2017-07-13,-26.0,999.855,amzn +2017-07-13,-27.0,999.855,amzn +2017-07-14,172.0,161.12,nflx +2017-07-14,169.0,161.12,nflx +2017-07-14,27.0,1001.81,amzn +2017-07-14,27.0,1001.81,amzn +2017-07-17,-171.0,159.73,fb +2017-07-17,-172.0,159.73,fb +2017-07-17,-27.0,975.96,googl +2017-07-17,-28.0,975.96,googl +2017-07-18,172.0,162.86,fb +2017-07-18,169.0,162.86,fb +2017-07-18,28.0,986.95,googl +2017-07-18,27.0,986.95,googl +2017-07-20,-169.0,183.6,nflx +2017-07-20,-156.0,183.6,nflx +2017-07-20,-179.0,150.34,aapl +2017-07-20,-190.0,150.34,aapl +2017-07-20,-27.0,992.19,googl +2017-07-20,-28.0,992.19,googl +2017-07-21,156.0,188.54,nflx +2017-07-21,152.0,188.54,nflx +2017-07-21,-27.0,1025.67,amzn +2017-07-21,-27.0,1025.67,amzn +2017-07-21,-169.0,164.43,fb +2017-07-21,-174.0,164.43,fb +2017-07-21,28.0,993.84,googl +2017-07-21,28.0,993.84,googl +2017-07-24,-152.0,187.91,nflx +2017-07-24,-153.0,187.91,nflx +2017-07-24,27.0,1038.95,amzn +2017-07-24,27.0,1038.95,amzn +2017-07-24,174.0,166.0,fb +2017-07-24,173.0,166.0,fb +2017-07-24,190.0,152.09,aapl +2017-07-24,189.0,152.09,aapl +2017-07-25,-173.0,165.28,fb +2017-07-25,-175.0,165.28,fb +2017-07-25,-28.0,969.03,googl +2017-07-25,-29.0,969.03,googl +2017-07-26,153.0,189.08,nflx +2017-07-26,152.0,189.08,nflx +2017-07-26,175.0,165.61,fb +2017-07-26,174.0,165.61,fb +2017-07-27,-152.0,182.68,nflx +2017-07-27,-158.0,182.68,nflx +2017-07-27,-27.0,1046.0,amzn +2017-07-27,-27.0,1046.0,amzn +2017-07-27,-189.0,150.56,aapl +2017-07-27,-192.0,150.56,aapl +2017-07-28,158.0,184.04,nflx +2017-07-28,156.0,184.04,nflx +2017-07-28,29.0,958.33,googl +2017-07-28,30.0,958.33,googl +2017-07-31,-156.0,181.66,nflx +2017-07-31,-158.0,181.66,nflx +2017-07-31,-174.0,169.25,fb +2017-07-31,-169.0,169.25,fb +2017-07-31,-30.0,945.5,googl +2017-07-31,-30.0,945.5,googl +2017-08-01,158.0,182.03,nflx +2017-08-01,155.0,182.03,nflx +2017-08-01,27.0,996.19,amzn +2017-08-01,28.0,996.19,amzn +2017-08-01,169.0,169.86,fb +2017-08-01,166.0,169.86,fb +2017-08-01,192.0,150.05,aapl +2017-08-01,188.0,150.05,aapl +2017-08-01,30.0,946.56,googl +2017-08-01,29.0,946.56,googl +2017-08-02,-155.0,180.74,nflx +2017-08-02,-157.0,180.74,nflx +2017-08-02,-28.0,995.89,amzn +2017-08-02,-28.0,995.89,amzn +2017-08-02,-166.0,169.25,fb +2017-08-02,-167.0,169.25,fb +2017-08-03,-188.0,155.57,aapl +2017-08-03,-183.0,155.57,aapl +2017-08-03,-29.0,940.3,googl +2017-08-03,-30.0,940.3,googl +2017-08-04,157.0,180.27,nflx +2017-08-04,157.0,180.27,nflx +2017-08-04,28.0,987.58,amzn +2017-08-04,28.0,987.58,amzn +2017-08-04,167.0,169.62,fb +2017-08-04,167.0,169.62,fb +2017-08-04,183.0,156.39,aapl +2017-08-04,181.0,156.39,aapl +2017-08-04,30.0,945.79,googl +2017-08-04,30.0,945.79,googl +2017-08-07,-30.0,945.75,googl +2017-08-07,-30.0,945.75,googl +2017-08-08,-157.0,178.36,nflx +2017-08-08,-160.0,178.36,nflx +2017-08-08,-167.0,171.23,fb +2017-08-08,-167.0,171.23,fb +2017-08-09,-28.0,982.01,amzn +2017-08-09,-29.0,982.01,amzn +2017-08-10,-181.0,155.27,aapl +2017-08-10,-183.0,155.27,aapl +2017-08-11,160.0,171.4,nflx +2017-08-11,161.0,171.4,nflx +2017-08-11,29.0,967.99,amzn +2017-08-11,28.0,967.99,amzn +2017-08-11,167.0,168.08,fb +2017-08-11,165.0,168.08,fb +2017-08-11,183.0,157.48,aapl +2017-08-11,176.0,157.48,aapl +2017-08-11,30.0,930.09,googl +2017-08-11,29.0,930.09,googl +2017-08-14,-161.0,171.0,nflx +2017-08-14,-163.0,171.0,nflx +2017-08-15,-28.0,982.74,amzn +2017-08-15,-28.0,982.74,amzn +2017-08-15,-29.0,938.08,googl +2017-08-15,-30.0,938.08,googl +2017-08-16,163.0,169.98,nflx +2017-08-16,166.0,169.98,nflx +2017-08-16,-165.0,170.0,fb +2017-08-16,-166.0,170.0,fb +2017-08-16,-176.0,160.95,aapl +2017-08-16,-175.0,160.95,aapl +2017-08-16,30.0,944.27,googl +2017-08-16,29.0,944.27,googl +2017-08-17,-166.0,166.09,nflx +2017-08-17,-170.0,166.09,nflx +2017-08-17,-29.0,927.66,googl +2017-08-17,-30.0,927.66,googl +2017-08-18,170.0,166.54,nflx +2017-08-18,166.0,166.54,nflx +2017-08-18,166.0,167.41,fb +2017-08-18,165.0,167.41,fb +2017-08-22,28.0,966.9,amzn +2017-08-22,28.0,966.9,amzn +2017-08-22,175.0,159.78,aapl +2017-08-22,173.0,159.78,aapl +2017-08-22,30.0,940.4,googl +2017-08-22,29.0,940.4,googl +2017-08-23,-166.0,169.06,nflx +2017-08-23,-166.0,169.06,nflx +2017-08-23,-28.0,958.0,amzn +2017-08-23,-29.0,958.0,amzn +2017-08-23,-165.0,168.71,fb +2017-08-23,-166.0,168.71,fb +2017-08-24,-173.0,159.27,aapl +2017-08-24,-176.0,159.27,aapl +2017-08-24,-29.0,936.89,googl +2017-08-24,-29.0,936.89,googl +2017-08-25,176.0,159.86,aapl +2017-08-25,174.0,159.86,aapl +2017-08-28,166.0,167.12,nflx +2017-08-28,165.0,167.12,nflx +2017-08-28,29.0,946.02,amzn +2017-08-28,29.0,946.02,amzn +2017-08-28,166.0,167.24,fb +2017-08-28,165.0,167.24,fb +2017-08-29,29.0,935.75,googl +2017-08-29,29.0,935.75,googl +2017-09-01,-29.0,978.25,amzn +2017-09-01,-29.0,978.25,amzn +2017-09-01,-29.0,951.99,googl +2017-09-01,-30.0,951.99,googl +2017-09-05,-165.0,174.52,nflx +2017-09-05,-164.0,174.52,nflx +2017-09-05,-165.0,170.72,fb +2017-09-05,-167.0,170.72,fb +2017-09-05,-174.0,162.08,aapl +2017-09-05,-176.0,162.08,aapl +2017-09-06,164.0,179.25,nflx +2017-09-06,158.0,179.25,nflx +2017-09-06,29.0,967.8,amzn +2017-09-06,29.0,967.8,amzn +2017-09-06,167.0,172.09,fb +2017-09-06,165.0,172.09,fb +2017-09-06,30.0,942.02,googl +2017-09-06,30.0,942.02,googl +2017-09-07,-158.0,179.0,nflx +2017-09-07,-159.0,179.0,nflx +2017-09-08,-29.0,965.9,amzn +2017-09-08,-29.0,965.9,amzn +2017-09-08,-165.0,170.95,fb +2017-09-08,-168.0,170.95,fb +2017-09-08,-30.0,941.41,googl +2017-09-08,-30.0,941.41,googl +2017-09-11,159.0,181.74,nflx +2017-09-11,156.0,181.74,nflx +2017-09-11,29.0,977.96,amzn +2017-09-11,29.0,977.96,amzn +2017-09-11,168.0,173.51,fb +2017-09-11,163.0,173.51,fb +2017-09-11,176.0,161.5,aapl +2017-09-11,175.0,161.5,aapl +2017-09-11,30.0,943.29,googl +2017-09-11,30.0,943.29,googl +2017-09-12,-163.0,172.96,fb +2017-09-12,-166.0,172.96,fb +2017-09-12,-175.0,160.82,aapl +2017-09-12,-179.0,160.82,aapl +2017-09-13,-156.0,183.64,nflx +2017-09-13,-157.0,183.64,nflx +2017-09-13,166.0,173.05,fb +2017-09-13,167.0,173.05,fb +2017-09-14,-29.0,992.21,amzn +2017-09-14,-29.0,992.21,amzn +2017-09-14,-167.0,170.96,fb +2017-09-14,-169.0,170.96,fb +2017-09-14,-30.0,940.13,googl +2017-09-14,-30.0,940.13,googl +2017-09-15,169.0,171.58,fb +2017-09-15,167.0,171.58,fb +2017-09-15,179.0,159.88,aapl +2017-09-15,179.0,159.88,aapl +2017-09-18,157.0,184.62,nflx +2017-09-18,155.0,184.62,nflx +2017-09-18,-167.0,170.01,fb +2017-09-18,-168.0,170.01,fb +2017-09-18,-179.0,158.67,aapl +2017-09-18,-180.0,158.67,aapl +2017-09-19,168.0,172.52,fb +2017-09-19,165.0,172.52,fb +2017-09-19,180.0,158.73,aapl +2017-09-19,180.0,158.73,aapl +2017-09-19,30.0,936.86,googl +2017-09-19,30.0,936.86,googl +2017-09-20,-155.0,185.51,nflx +2017-09-20,-154.0,185.51,nflx +2017-09-20,29.0,973.21,amzn +2017-09-20,29.0,973.21,amzn +2017-09-20,-165.0,172.17,fb +2017-09-20,-166.0,172.17,fb +2017-09-20,-180.0,156.07,aapl +2017-09-20,-184.0,156.07,aapl +2017-09-21,154.0,188.78,nflx +2017-09-21,151.0,188.78,nflx +2017-09-21,-29.0,964.65,amzn +2017-09-21,-29.0,964.65,amzn +2017-09-22,-151.0,187.35,nflx +2017-09-22,-152.0,187.35,nflx +2017-09-22,-30.0,943.26,googl +2017-09-22,-30.0,943.26,googl +2017-09-26,152.0,179.38,nflx +2017-09-26,154.0,179.38,nflx +2017-09-26,166.0,164.21,fb +2017-09-26,168.0,164.21,fb +2017-09-26,184.0,153.14,aapl +2017-09-26,180.0,153.14,aapl +2017-09-26,30.0,937.43,googl +2017-09-26,29.0,937.43,googl +2017-09-27,29.0,950.87,amzn +2017-09-27,29.0,950.87,amzn +2017-09-28,-154.0,180.7,nflx +2017-09-28,-156.0,180.7,nflx +2017-09-28,-180.0,153.28,aapl +2017-09-28,-184.0,153.28,aapl +2017-09-29,156.0,181.35,nflx +2017-09-29,156.0,181.35,nflx +2017-09-29,184.0,154.12,aapl +2017-09-29,183.0,154.12,aapl +2017-10-02,-156.0,177.01,nflx +2017-10-02,-161.0,177.01,nflx +2017-10-02,-29.0,959.19,amzn +2017-10-02,-29.0,959.19,amzn +2017-10-02,-168.0,169.47,fb +2017-10-02,-168.0,169.47,fb +2017-10-02,-183.0,153.81,aapl +2017-10-02,-185.0,153.81,aapl +2017-10-02,-29.0,967.47,googl +2017-10-02,-29.0,967.47,googl +2017-10-03,161.0,179.19,nflx +2017-10-03,157.0,179.19,nflx +2017-10-03,168.0,169.96,fb +2017-10-03,166.0,169.96,fb +2017-10-03,185.0,154.48,aapl +2017-10-03,183.0,154.48,aapl +2017-10-03,29.0,972.08,googl +2017-10-03,29.0,972.08,googl +2017-10-04,29.0,965.45,amzn +2017-10-04,29.0,965.45,amzn +2017-10-04,-166.0,168.42,fb +2017-10-04,-168.0,168.42,fb +2017-10-04,-183.0,153.4508,aapl +2017-10-04,-185.0,153.4508,aapl +2017-10-04,-29.0,966.78,googl +2017-10-04,-29.0,966.78,googl +2017-10-05,168.0,171.24,fb +2017-10-05,166.0,171.24,fb +2017-10-05,185.0,155.39,aapl +2017-10-05,183.0,155.39,aapl +2017-10-05,29.0,985.19,googl +2017-10-05,28.0,985.19,googl +2017-10-06,-183.0,155.3,aapl +2017-10-06,-187.0,155.3,aapl +2017-10-09,-157.0,196.87,nflx +2017-10-09,-149.0,196.87,nflx +2017-10-09,187.0,155.84,aapl +2017-10-09,188.0,155.84,aapl +2017-10-09,-28.0,992.31,googl +2017-10-09,-29.0,992.31,googl +2017-10-10,-29.0,987.2,amzn +2017-10-10,-29.0,987.2,amzn +2017-10-10,-166.0,171.59,fb +2017-10-10,-171.0,171.59,fb +2017-10-11,29.0,995.0,amzn +2017-10-11,29.0,995.0,amzn +2017-10-11,171.0,172.74,fb +2017-10-11,169.0,172.74,fb +2017-10-11,29.0,1005.65,googl +2017-10-11,29.0,1005.65,googl +2017-10-12,149.0,195.86,nflx +2017-10-12,150.0,195.86,nflx +2017-10-12,-169.0,172.55,fb +2017-10-12,-170.0,172.55,fb +2017-10-12,-188.0,156.0,aapl +2017-10-12,-189.0,156.0,aapl +2017-10-13,170.0,173.74,fb +2017-10-13,170.0,173.74,fb +2017-10-13,189.0,156.99,aapl +2017-10-13,188.0,156.99,aapl +2017-10-17,-150.0,199.48,nflx +2017-10-17,-150.0,199.48,nflx +2017-10-18,-29.0,997.0,amzn +2017-10-18,-30.0,997.0,amzn +2017-10-18,-170.0,176.03,fb +2017-10-18,-170.0,176.03,fb +2017-10-18,-188.0,159.76,aapl +2017-10-18,-187.0,159.76,aapl +2017-10-19,-29.0,1001.84,googl +2017-10-19,-29.0,1001.84,googl +2017-10-20,170.0,174.98,fb +2017-10-20,168.0,174.98,fb +2017-10-20,187.0,156.16,aapl +2017-10-20,188.0,156.16,aapl +2017-10-20,29.0,1005.07,googl +2017-10-20,29.0,1005.07,googl +2017-10-23,-168.0,171.27,fb +2017-10-23,-172.0,171.27,fb +2017-10-23,-29.0,985.54,googl +2017-10-23,-29.0,985.54,googl +2017-10-24,150.0,196.02,nflx +2017-10-24,148.0,196.02,nflx +2017-10-24,30.0,975.9,amzn +2017-10-24,29.0,975.9,amzn +2017-10-24,172.0,171.8,fb +2017-10-24,169.0,171.8,fb +2017-10-24,29.0,988.49,googl +2017-10-24,29.0,988.49,googl +2017-10-25,-148.0,193.77,nflx +2017-10-25,-151.0,193.77,nflx +2017-10-25,-29.0,972.91,amzn +2017-10-25,-30.0,972.91,amzn +2017-10-25,-169.0,170.6,fb +2017-10-25,-171.0,170.6,fb +2017-10-25,-188.0,156.405,aapl +2017-10-25,-187.0,156.405,aapl +2017-10-26,151.0,195.21,nflx +2017-10-26,149.0,195.21,nflx +2017-10-26,171.0,170.63,fb +2017-10-26,170.0,170.63,fb +2017-10-26,187.0,157.41,aapl +2017-10-26,185.0,157.41,aapl +2017-10-26,-29.0,991.42,googl +2017-10-26,-29.0,991.42,googl +2017-10-27,30.0,1100.95,amzn +2017-10-27,26.0,1100.95,amzn +2017-10-27,29.0,1033.67,googl +2017-10-27,28.0,1033.67,googl +2017-10-30,-149.0,198.37,nflx +2017-10-30,-155.0,198.37,nflx +2017-10-30,-28.0,1033.13,googl +2017-10-30,-29.0,1033.13,googl +2017-10-31,-26.0,1105.28,amzn +2017-10-31,-28.0,1105.28,amzn +2017-11-01,155.0,198.0,nflx +2017-11-01,156.0,198.0,nflx +2017-11-01,-185.0,166.89,aapl +2017-11-01,-186.0,166.89,aapl +2017-11-01,29.0,1042.6,googl +2017-11-01,29.0,1042.6,googl +2017-11-02,-170.0,178.92,fb +2017-11-02,-174.0,178.92,fb +2017-11-02,186.0,168.11,aapl +2017-11-02,185.0,168.11,aapl +2017-11-03,28.0,1111.6,amzn +2017-11-03,27.0,1111.6,amzn +2017-11-06,174.0,180.17,fb +2017-11-06,174.0,180.17,fb +2017-11-06,-29.0,1042.68,googl +2017-11-06,-30.0,1042.68,googl +2017-11-07,-156.0,195.89,nflx +2017-11-07,-160.0,195.89,nflx +2017-11-07,30.0,1052.39,googl +2017-11-07,29.0,1052.39,googl +2017-11-09,-174.0,179.3,fb +2017-11-09,-175.0,179.3,fb +2017-11-09,-29.0,1047.72,googl +2017-11-09,-30.0,1047.72,googl +2017-11-10,-27.0,1125.35,amzn +2017-11-10,-27.0,1125.35,amzn +2017-11-10,-185.0,174.67,aapl +2017-11-10,-180.0,174.67,aapl +2017-11-13,160.0,195.08,nflx +2017-11-13,160.0,195.08,nflx +2017-11-13,27.0,1129.17,amzn +2017-11-13,27.0,1129.17,amzn +2017-11-13,175.0,178.77,fb +2017-11-13,174.0,178.77,fb +2017-11-14,-174.0,178.07,fb +2017-11-14,-176.0,178.07,fb +2017-11-14,30.0,1041.64,googl +2017-11-14,30.0,1041.64,googl +2017-11-15,-160.0,192.12,nflx +2017-11-15,-162.0,192.12,nflx +2017-11-15,-27.0,1126.69,amzn +2017-11-15,-27.0,1126.69,amzn +2017-11-15,-30.0,1036.41,googl +2017-11-15,-30.0,1036.41,googl +2017-11-16,162.0,195.51,nflx +2017-11-16,158.0,195.51,nflx +2017-11-16,27.0,1137.29,amzn +2017-11-16,27.0,1137.29,amzn +2017-11-16,176.0,179.59,fb +2017-11-16,172.0,179.59,fb +2017-11-16,180.0,171.1,aapl +2017-11-16,181.0,171.1,aapl +2017-11-16,30.0,1048.47,googl +2017-11-16,29.0,1048.47,googl +2017-11-17,-158.0,193.2,nflx +2017-11-17,-162.0,193.2,nflx +2017-11-17,-27.0,1129.88,amzn +2017-11-17,-27.0,1129.88,amzn +2017-11-17,-172.0,179.0,fb +2017-11-17,-175.0,179.0,fb +2017-11-17,-181.0,170.15,aapl +2017-11-17,-184.0,170.15,aapl +2017-11-17,-29.0,1035.89,googl +2017-11-17,-30.0,1035.89,googl +2017-11-20,162.0,194.1,nflx +2017-11-20,160.0,194.1,nflx +2017-11-21,27.0,1139.49,amzn +2017-11-21,27.0,1139.49,amzn +2017-11-21,175.0,181.86,fb +2017-11-21,171.0,181.86,fb +2017-11-21,184.0,173.14,aapl +2017-11-21,179.0,173.14,aapl +2017-11-21,30.0,1050.3,googl +2017-11-21,29.0,1050.3,googl +2017-11-22,-171.0,180.87,fb +2017-11-22,-174.0,180.87,fb +2017-11-24,-160.0,195.75,nflx +2017-11-24,-162.0,195.75,nflx +2017-11-24,174.0,182.78,fb +2017-11-24,173.0,182.78,fb +2017-11-27,-179.0,174.09,aapl +2017-11-27,-183.0,174.09,aapl +2017-11-28,162.0,199.18,nflx +2017-11-28,160.0,199.18,nflx +2017-11-28,-27.0,1193.6,amzn +2017-11-28,-26.0,1193.6,amzn +2017-11-28,-173.0,182.42,fb +2017-11-28,-175.0,182.42,fb +2017-11-28,-29.0,1063.29,googl +2017-11-28,-30.0,1063.29,googl +2017-11-29,-160.0,188.15,nflx +2017-11-29,-170.0,188.15,nflx +2017-11-30,26.0,1176.75,amzn +2017-11-30,26.0,1176.75,amzn +2017-11-30,175.0,177.18,fb +2017-11-30,174.0,177.18,fb +2017-11-30,183.0,171.85,aapl +2017-11-30,180.0,171.85,aapl +2017-12-01,-26.0,1162.35,amzn +2017-12-01,-26.0,1162.35,amzn +2017-12-01,-174.0,175.1,fb +2017-12-01,-178.0,175.1,fb +2017-12-01,-180.0,171.05,aapl +2017-12-01,-182.0,171.05,aapl +2017-12-05,170.0,184.21,nflx +2017-12-05,165.0,184.21,nflx +2017-12-05,26.0,1141.57,amzn +2017-12-05,26.0,1141.57,amzn +2017-12-05,178.0,172.83,fb +2017-12-05,176.0,172.83,fb +2017-12-05,30.0,1019.6,googl +2017-12-05,29.0,1019.6,googl +2017-12-07,-165.0,185.2,nflx +2017-12-07,-166.0,185.2,nflx +2017-12-07,182.0,169.452,aapl +2017-12-07,182.0,169.452,aapl +2017-12-08,166.0,188.54,nflx +2017-12-08,164.0,188.54,nflx +2017-12-08,-176.0,179.0,fb +2017-12-08,-173.0,179.0,fb +2017-12-08,-182.0,169.37,aapl +2017-12-08,-183.0,169.37,aapl +2017-12-11,-164.0,186.22,nflx +2017-12-11,-167.0,186.22,nflx +2017-12-11,173.0,179.04,fb +2017-12-11,174.0,179.04,fb +2017-12-11,183.0,172.67,aapl +2017-12-11,180.0,172.67,aapl +2017-12-12,-26.0,1165.08,amzn +2017-12-12,-26.0,1165.08,amzn +2017-12-12,-174.0,176.96,fb +2017-12-12,-176.0,176.96,fb +2017-12-12,-180.0,171.7,aapl +2017-12-12,-182.0,171.7,aapl +2017-12-12,-29.0,1048.77,googl +2017-12-12,-29.0,1048.77,googl +2017-12-13,167.0,187.86,nflx +2017-12-13,165.0,187.86,nflx +2017-12-13,176.0,178.3,fb +2017-12-13,174.0,178.3,fb +2017-12-13,182.0,172.27,aapl +2017-12-13,180.0,172.27,aapl +2017-12-13,29.0,1051.39,googl +2017-12-13,29.0,1051.39,googl +2017-12-14,26.0,1174.26,amzn +2017-12-14,26.0,1174.26,amzn +2017-12-14,-180.0,172.22,aapl +2017-12-14,-181.0,172.22,aapl +2017-12-15,181.0,173.87,aapl +2017-12-15,180.0,173.87,aapl +2017-12-19,-165.0,187.02,nflx +2017-12-19,-170.0,187.02,nflx +2017-12-19,-26.0,1187.38,amzn +2017-12-19,-26.0,1187.38,amzn +2017-12-19,-174.0,179.51,fb +2017-12-19,-177.0,179.51,fb +2017-12-19,-180.0,174.54,aapl +2017-12-19,-183.0,174.54,aapl +2017-12-19,-29.0,1079.78,googl +2017-12-19,-29.0,1079.78,googl +2017-12-20,170.0,188.82,nflx +2017-12-20,167.0,188.82,nflx +2017-12-21,-167.0,188.62,nflx +2017-12-21,-167.0,188.62,nflx +2017-12-21,183.0,175.01,aapl +2017-12-21,180.0,175.01,aapl +2017-12-22,167.0,189.94,nflx +2017-12-22,166.0,189.94,nflx +2017-12-26,-166.0,187.76,nflx +2017-12-26,-168.0,187.76,nflx +2017-12-26,26.0,1176.76,amzn +2017-12-26,26.0,1176.76,amzn +2017-12-26,-180.0,170.57,aapl +2017-12-26,-184.0,170.57,aapl +2017-12-27,177.0,177.62,fb +2017-12-27,176.0,177.62,fb +2017-12-27,184.0,170.6,aapl +2017-12-27,183.0,170.6,aapl +2017-12-28,168.0,192.71,nflx +2017-12-28,162.0,192.71,nflx +2017-12-29,-162.0,191.96,nflx +2017-12-29,-164.0,191.96,nflx +2017-12-29,-26.0,1169.47,amzn +2017-12-29,-26.0,1169.47,amzn +2017-12-29,-176.0,176.46,fb +2017-12-29,-178.0,176.46,fb +2017-12-29,-183.0,169.23,aapl +2017-12-29,-186.0,169.23,aapl diff --git a/alphapy/frame.py b/alphapy/frame.py index 106b5d1..aed19da 100644 --- a/alphapy/frame.py +++ b/alphapy/frame.py @@ -228,8 +228,8 @@ def load_frames(group, directory, extension, separator, splits=False): The delimiter between fields in the file. splits : bool, optional If ``True``, then all the members of the group are stored in - one file. If ``False``, then the data are stored in separate - files corresponding with each member. + separate files corresponding with each member. If ``False``, + then the data are stored in a single file. Returns ------- diff --git a/alphapy/globals.py b/alphapy/globals.py index bab9462..5b0a71b 100644 --- a/alphapy/globals.py +++ b/alphapy/globals.py @@ -65,7 +65,20 @@ # Dictionaries # -MULTIPLIERS = {'stock' : 1.0} +MULTIPLIERS = {'crypto' : 1.0, + 'stock' : 1.0} + +# +# Pandas Time Offset Aliases +# + +PD_INTRADAY_OFFSETS = ['H', 'T', 'min', 'S', 'L', 'ms', 'U', 'us', 'N'] + +# +# Pandas Web Reader Feeds +# + +PD_WEB_DATA_FEEDS = ['google', 'quandl', 'yahoo'] # diff --git a/alphapy/market_flow.py b/alphapy/market_flow.py index aec700c..58c8f49 100644 --- a/alphapy/market_flow.py +++ b/alphapy/market_flow.py @@ -29,7 +29,7 @@ from alphapy.alias import Alias from alphapy.analysis import Analysis from alphapy.analysis import run_analysis -from alphapy.data import get_feed_data +from alphapy.data import get_market_data from alphapy.globals import PSEP, SSEP from alphapy.group import Group from alphapy.market_variables import Variable @@ -90,17 +90,25 @@ def get_market_config(): # Section: market [this section must be first] specs['forecast_period'] = cfg['market']['forecast_period'] - specs['fractal'] = cfg['market']['fractal'] + fractal = cfg['market']['fractal'] + try: + test_interval = pd.to_timedelta(fractal) + except: + logger.info("Pandas offset alias [%s] is invalid for resampling", + fractal) + specs['fractal'] = fractal specs['lag_period'] = cfg['market']['lag_period'] specs['leaders'] = cfg['market']['leaders'] specs['data_history'] = cfg['market']['data_history'] specs['predict_history'] = cfg['market']['predict_history'] + specs['resample_data'] = cfg['market']['resample_data'] specs['schema'] = cfg['market']['schema'] + specs['subject'] = cfg['market']['subject'] specs['target_group'] = cfg['market']['target_group'] # Create the subject/schema/fractal namespace - sspecs = ['stock', specs['schema'], specs['fractal']] + sspecs = [specs['subject'], specs['schema'], specs['fractal']] space = Space(*sspecs) # Section: features @@ -168,7 +176,9 @@ def get_market_config(): logger.info('lag_period = %d', specs['lag_period']) logger.info('leaders = %s', specs['leaders']) logger.info('predict_history = %s', specs['predict_history']) + logger.info('resample_data = %r', specs['resample_data']) logger.info('schema = %s', specs['schema']) + logger.info('subject = %s', specs['subject']) logger.info('system = %s', specs['system']) logger.info('target_group = %s', specs['target_group']) @@ -221,6 +231,7 @@ def market_pipeline(model, market_specs): lag_period = market_specs['lag_period'] leaders = market_specs['leaders'] predict_history = market_specs['predict_history'] + resample_data = market_specs['resample_data'] target_group = market_specs['target_group'] # Get the system specifications @@ -251,10 +262,12 @@ def market_pipeline(model, market_specs): # predict_history resets to the actual history obtained. lookback = predict_history if predict_mode else data_history - new_history = get_feed_data(group, lookback) + new_history = get_market_data(model, group, lookback, resample_data) if new_history < data_history: logger.info("Maximum Data History is %d, not %d", new_history, data_history) + if new_history == 0: + raise ValueError("Could not get market data from source") # Apply the features to all of the frames diff --git a/alphapy/system.py b/alphapy/system.py index 3f53940..e7117fe 100644 --- a/alphapy/system.py +++ b/alphapy/system.py @@ -263,7 +263,8 @@ def long_short(system, name, space, quantity): # Function open_range_breakout # -def open_range_breakout(name, space, quantity, t1=3, t2=12): +def open_range_breakout(name, space, quantity, t1=3, t2=12, + long_only=False): r"""Run an Opening Range Breakout (ORB) system. An ORB system is an intraday strategy that waits for price to @@ -329,7 +330,7 @@ def open_range_breakout(name, space, quantity, t1=3, t2=12): tradelist.append((dt, [name, Orders.le, quantity, hh])) inlong = True traded = True - if l < ll and not traded: + if l < ll and not traded and not long_only: # short breakout triggers tradelist.append((dt, [name, Orders.se, -quantity, ll])) inshort = True diff --git a/docs/tutorials/closer_market.yml b/docs/tutorials/closer_market.yml index 635824e..d01f3bd 100644 --- a/docs/tutorials/closer_market.yml +++ b/docs/tutorials/closer_market.yml @@ -1,10 +1,13 @@ market: - data_history : 1000 + data_history : 500 forecast_period : 1 fractal : 1d + lag_period : 1 leaders : [] predict_history : 50 - schema : prices + resample_data : False + schema : quandl_wiki + subject : stock target_group : faang system: diff --git a/docs/tutorials/market.rst b/docs/tutorials/market.rst index a227c4a..06d0fc6 100644 --- a/docs/tutorials/market.rst +++ b/docs/tutorials/market.rst @@ -85,7 +85,7 @@ what we are trying to predict. **Step 2**: Now, let's run MarketFlow:: - mflow --pdate 2017-01-01 + mflow --pdate 2017-10-01 As ``mflow`` runs, you will see the progress of the workflow, and the logging output is saved in ``market_flow.log``. When the @@ -100,7 +100,9 @@ with a different datestamp:: ├── model.yml └── data └── input + ├── test_20170420.csv ├── test.csv + ├── train_20170420.csv ├── train.csv └── model ├── feature_map_20170420.pkl diff --git a/docs/tutorials/rrover_market.yml b/docs/tutorials/rrover_market.yml index 7f73975..3ed085e 100644 --- a/docs/tutorials/rrover_market.yml +++ b/docs/tutorials/rrover_market.yml @@ -1,10 +1,13 @@ market: - data_history : 2000 + data_history : 500 forecast_period : 1 fractal : 1d + lag_period : 1 leaders : ['gap', 'gapbadown', 'gapbaup', 'gapdown', 'gapup'] predict_history : 100 - schema : prices + resample_data : False + schema : yahoo + subject : stock target_group : test groups: diff --git a/docs/user_guide/alphapy.log b/docs/user_guide/alphapy.log index 0f81e31..053f136 100644 --- a/docs/user_guide/alphapy.log +++ b/docs/user_guide/alphapy.log @@ -1,354 +1,361 @@ -[04/18/17 12:08:34] INFO ******************************************************************************** -[04/18/17 12:08:34] INFO AlphaPy Start -[04/18/17 12:08:34] INFO ******************************************************************************** -[04/18/17 12:08:34] INFO Model Configuration -[04/18/17 12:08:34] INFO No Treatments Found -[04/18/17 12:08:34] INFO MODEL PARAMETERS: -[04/18/17 12:08:34] INFO algorithms = ['RF', 'XGB'] -[04/18/17 12:08:34] INFO balance_classes = True -[04/18/17 12:08:34] INFO calibration = False -[04/18/17 12:08:34] INFO cal_type = sigmoid -[04/18/17 12:08:34] INFO calibration_plot = False -[04/18/17 12:08:34] INFO clustering = True -[04/18/17 12:08:34] INFO cluster_inc = 3 -[04/18/17 12:08:34] INFO cluster_max = 30 -[04/18/17 12:08:34] INFO cluster_min = 3 -[04/18/17 12:08:34] INFO confusion_matrix = True -[04/18/17 12:08:34] INFO counts = True -[04/18/17 12:08:34] INFO cv_folds = 3 -[04/18/17 12:08:34] INFO directory = /Users/markconway/Projects/Titanic -[04/18/17 12:08:34] INFO extension = csv -[04/18/17 12:08:34] INFO drop = ['PassengerId'] -[04/18/17 12:08:34] INFO encoder = -[04/18/17 12:08:34] INFO esr = 20 -[04/18/17 12:08:34] INFO factors = [] -[04/18/17 12:08:34] INFO features [X] = * -[04/18/17 12:08:34] INFO feature_selection = False -[04/18/17 12:08:34] INFO fs_percentage = 50 -[04/18/17 12:08:34] INFO fs_score_func = -[04/18/17 12:08:34] INFO fs_uni_grid = [5, 10, 15, 20, 25] -[04/18/17 12:08:34] INFO grid_search = True -[04/18/17 12:08:34] INFO gs_iters = 50 -[04/18/17 12:08:34] INFO gs_random = True -[04/18/17 12:08:34] INFO gs_sample = False -[04/18/17 12:08:34] INFO gs_sample_pct = 0.200000 -[04/18/17 12:08:34] INFO importances = True -[04/18/17 12:08:34] INFO interactions = True -[04/18/17 12:08:34] INFO isomap = False -[04/18/17 12:08:34] INFO iso_components = 2 -[04/18/17 12:08:34] INFO iso_neighbors = 5 -[04/18/17 12:08:34] INFO isample_pct = 10 -[04/18/17 12:08:34] INFO learning_curve = True -[04/18/17 12:08:34] INFO logtransform = False -[04/18/17 12:08:34] INFO lv_remove = True -[04/18/17 12:08:34] INFO lv_threshold = 0.100000 -[04/18/17 12:08:34] INFO model_type = -[04/18/17 12:08:34] INFO n_estimators = 51 -[04/18/17 12:08:34] INFO n_jobs = -1 -[04/18/17 12:08:34] INFO ngrams_max = 3 -[04/18/17 12:08:34] INFO numpy = True -[04/18/17 12:08:34] INFO pca = False -[04/18/17 12:08:34] INFO pca_inc = 1 -[04/18/17 12:08:34] INFO pca_max = 10 -[04/18/17 12:08:34] INFO pca_min = 2 -[04/18/17 12:08:34] INFO pca_whiten = False -[04/18/17 12:08:34] INFO poly_degree = 5 -[04/18/17 12:08:34] INFO pvalue_level = 0.010000 -[04/18/17 12:08:34] INFO rfe = True -[04/18/17 12:08:34] INFO rfe_step = 3 -[04/18/17 12:08:34] INFO roc_curve = True -[04/18/17 12:08:34] INFO rounding = 2 -[04/18/17 12:08:34] INFO sampling = False -[04/18/17 12:08:34] INFO sampling_method = -[04/18/17 12:08:34] INFO sampling_ratio = 0.500000 -[04/18/17 12:08:34] INFO scaler_option = True -[04/18/17 12:08:34] INFO scaler_type = -[04/18/17 12:08:34] INFO scipy = False -[04/18/17 12:08:34] INFO scorer = roc_auc -[04/18/17 12:08:34] INFO seed = 42 -[04/18/17 12:08:34] INFO sentinel = -1 -[04/18/17 12:08:34] INFO separator = , -[04/18/17 12:08:34] INFO shuffle = False -[04/18/17 12:08:34] INFO split = 0.400000 -[04/18/17 12:08:34] INFO submission_file = gender_submission -[04/18/17 12:08:34] INFO submit_probas = False -[04/18/17 12:08:34] INFO target [y] = Survived -[04/18/17 12:08:34] INFO target_value = 1 -[04/18/17 12:08:34] INFO treatments = None -[04/18/17 12:08:34] INFO tsne = False -[04/18/17 12:08:34] INFO tsne_components = 2 -[04/18/17 12:08:34] INFO tsne_learn_rate = 1000.000000 -[04/18/17 12:08:34] INFO tsne_perplexity = 30.000000 -[04/18/17 12:08:34] INFO vectorize = False -[04/18/17 12:08:34] INFO verbosity = 0 -[04/18/17 12:08:34] INFO Creating Model -[04/18/17 12:08:34] INFO Calling Pipeline -[04/18/17 12:08:34] INFO Training Pipeline -[04/18/17 12:08:34] INFO Loading Data -[04/18/17 12:08:34] INFO Loading data from /Users/markconway/Projects/Titanic/input/train.csv -[04/18/17 12:08:34] INFO Found target Survived in data frame -[04/18/17 12:08:34] INFO Dropping target Survived from data frame -[04/18/17 12:08:34] INFO Loading Data -[04/18/17 12:08:34] INFO Loading data from /Users/markconway/Projects/Titanic/input/test.csv -[04/18/17 12:08:34] INFO Target Survived not found in partition Partition.test -[04/18/17 12:08:34] INFO Saving New Features in Model -[04/18/17 12:08:34] INFO Dropping Features: ['PassengerId'] -[04/18/17 12:08:34] INFO Saving New Features in Model -[04/18/17 12:08:34] INFO Original Feature Statistics -[04/18/17 12:08:34] INFO Number of Training Rows : 891 -[04/18/17 12:08:34] INFO Number of Training Columns : 10 -[04/18/17 12:08:34] INFO Unique Training Values for Survived : [0 1] -[04/18/17 12:08:34] INFO Unique Training Counts for Survived : [549 342] -[04/18/17 12:08:34] INFO Number of Testing Rows : 418 -[04/18/17 12:08:34] INFO Number of Testing Columns : 10 -[04/18/17 12:08:34] INFO Original Features : Index(['Pclass', 'Name', 'Sex', 'Age', 'SibSp', 'Parch', 'Ticket', 'Fare', - 'Cabin', 'Embarked'], +[12/30/17 23:17:49] INFO ******************************************************************************** +[12/30/17 23:17:49] INFO AlphaPy Start +[12/30/17 23:17:49] INFO ******************************************************************************** +[12/30/17 23:17:49] INFO Model Configuration +[12/30/17 23:17:49] INFO No Treatments Found +[12/30/17 23:17:49] INFO MODEL PARAMETERS: +[12/30/17 23:17:49] INFO algorithms = ['RF', 'XGB'] +[12/30/17 23:17:49] INFO balance_classes = True +[12/30/17 23:17:49] INFO calibration = False +[12/30/17 23:17:49] INFO cal_type = sigmoid +[12/30/17 23:17:49] INFO calibration_plot = False +[12/30/17 23:17:49] INFO clustering = True +[12/30/17 23:17:49] INFO cluster_inc = 3 +[12/30/17 23:17:49] INFO cluster_max = 30 +[12/30/17 23:17:49] INFO cluster_min = 3 +[12/30/17 23:17:49] INFO confusion_matrix = True +[12/30/17 23:17:49] INFO counts = True +[12/30/17 23:17:49] INFO cv_folds = 3 +[12/30/17 23:17:49] INFO directory = . +[12/30/17 23:17:49] INFO extension = csv +[12/30/17 23:17:49] INFO drop = ['PassengerId'] +[12/30/17 23:17:49] INFO encoder = +[12/30/17 23:17:50] INFO esr = 20 +[12/30/17 23:17:50] INFO factors = [] +[12/30/17 23:17:50] INFO features [X] = * +[12/30/17 23:17:50] INFO feature_selection = False +[12/30/17 23:17:50] INFO fs_percentage = 50 +[12/30/17 23:17:50] INFO fs_score_func = +[12/30/17 23:17:50] INFO fs_uni_grid = [5, 10, 15, 20, 25] +[12/30/17 23:17:50] INFO grid_search = True +[12/30/17 23:17:50] INFO gs_iters = 50 +[12/30/17 23:17:50] INFO gs_random = True +[12/30/17 23:17:50] INFO gs_sample = False +[12/30/17 23:17:50] INFO gs_sample_pct = 0.200000 +[12/30/17 23:17:50] INFO importances = True +[12/30/17 23:17:50] INFO interactions = True +[12/30/17 23:17:50] INFO isomap = False +[12/30/17 23:17:50] INFO iso_components = 2 +[12/30/17 23:17:50] INFO iso_neighbors = 5 +[12/30/17 23:17:50] INFO isample_pct = 10 +[12/30/17 23:17:50] INFO learning_curve = True +[12/30/17 23:17:50] INFO logtransform = False +[12/30/17 23:17:50] INFO lv_remove = True +[12/30/17 23:17:50] INFO lv_threshold = 0.100000 +[12/30/17 23:17:50] INFO model_type = +[12/30/17 23:17:50] INFO n_estimators = 51 +[12/30/17 23:17:50] INFO n_jobs = -1 +[12/30/17 23:17:50] INFO ngrams_max = 3 +[12/30/17 23:17:50] INFO numpy = True +[12/30/17 23:17:50] INFO pca = False +[12/30/17 23:17:50] INFO pca_inc = 1 +[12/30/17 23:17:50] INFO pca_max = 10 +[12/30/17 23:17:50] INFO pca_min = 2 +[12/30/17 23:17:50] INFO pca_whiten = False +[12/30/17 23:17:50] INFO poly_degree = 5 +[12/30/17 23:17:50] INFO pvalue_level = 0.010000 +[12/30/17 23:17:50] INFO rfe = True +[12/30/17 23:17:50] INFO rfe_step = 3 +[12/30/17 23:17:50] INFO roc_curve = True +[12/30/17 23:17:50] INFO rounding = 2 +[12/30/17 23:17:50] INFO sampling = False +[12/30/17 23:17:50] INFO sampling_method = +[12/30/17 23:17:50] INFO sampling_ratio = 0.500000 +[12/30/17 23:17:50] INFO scaler_option = True +[12/30/17 23:17:50] INFO scaler_type = +[12/30/17 23:17:50] INFO scipy = False +[12/30/17 23:17:50] INFO scorer = roc_auc +[12/30/17 23:17:50] INFO seed = 42 +[12/30/17 23:17:50] INFO sentinel = -1 +[12/30/17 23:17:50] INFO separator = , +[12/30/17 23:17:50] INFO shuffle = False +[12/30/17 23:17:50] INFO split = 0.400000 +[12/30/17 23:17:50] INFO submission_file = gender_submission +[12/30/17 23:17:50] INFO submit_probas = False +[12/30/17 23:17:50] INFO target [y] = Survived +[12/30/17 23:17:50] INFO target_value = 1 +[12/30/17 23:17:50] INFO treatments = None +[12/30/17 23:17:50] INFO tsne = False +[12/30/17 23:17:50] INFO tsne_components = 2 +[12/30/17 23:17:50] INFO tsne_learn_rate = 1000.000000 +[12/30/17 23:17:50] INFO tsne_perplexity = 30.000000 +[12/30/17 23:17:50] INFO vectorize = False +[12/30/17 23:17:50] INFO verbosity = 0 +[12/30/17 23:17:50] INFO Creating directory ./data +[12/30/17 23:17:50] INFO Creating directory ./model +[12/30/17 23:17:50] INFO Creating directory ./output +[12/30/17 23:17:50] INFO Creating directory ./plots +[12/30/17 23:17:50] INFO Creating Model +[12/30/17 23:17:50] INFO Calling Pipeline +[12/30/17 23:17:50] INFO Training Pipeline +[12/30/17 23:17:50] INFO Loading Data +[12/30/17 23:17:50] INFO Loading data from ./input/train.csv +[12/30/17 23:17:50] INFO Found target Survived in data frame +[12/30/17 23:17:50] INFO Labels (y) found for Partition.train +[12/30/17 23:17:50] INFO Loading Data +[12/30/17 23:17:50] INFO Loading data from ./input/test.csv +[12/30/17 23:17:50] INFO Target Survived not found in Partition.test +[12/30/17 23:17:50] INFO Saving New Features in Model +[12/30/17 23:17:50] INFO Original Feature Statistics +[12/30/17 23:17:50] INFO Number of Training Rows : 891 +[12/30/17 23:17:50] INFO Number of Training Columns : 11 +[12/30/17 23:17:50] INFO Unique Training Values for Survived : [0 1] +[12/30/17 23:17:50] INFO Unique Training Counts for Survived : [549 342] +[12/30/17 23:17:50] INFO Number of Testing Rows : 418 +[12/30/17 23:17:50] INFO Number of Testing Columns : 11 +[12/30/17 23:17:50] INFO Original Features : Index(['PassengerId', 'Pclass', 'Name', 'Sex', 'Age', 'SibSp', 'Parch', + 'Ticket', 'Fare', 'Cabin', 'Embarked'], dtype='object') -[04/18/17 12:08:34] INFO Feature Count : 10 -[04/18/17 12:08:34] INFO Applying Treatments -[04/18/17 12:08:34] INFO New Feature Count : 10 -[04/18/17 12:08:34] INFO Saving New Features in Model -[04/18/17 12:08:34] INFO Creating Cross-Tabulations -[04/18/17 12:08:34] INFO Original Features : Index(['Pclass', 'Name', 'Sex', 'Age', 'SibSp', 'Parch', 'Ticket', 'Fare', +[12/30/17 23:17:50] INFO Feature Count : 11 +[12/30/17 23:17:50] INFO Applying Treatments +[12/30/17 23:17:50] INFO No Treatments Specified +[12/30/17 23:17:50] INFO New Feature Count : 11 +[12/30/17 23:17:50] INFO Dropping Features: ['PassengerId'] +[12/30/17 23:17:50] INFO Original Feature Count : 11 +[12/30/17 23:17:50] INFO Reduced Feature Count : 10 +[12/30/17 23:17:50] INFO Writing data frame to ./input/train_20171230.csv +[12/30/17 23:17:50] INFO Writing data frame to ./input/test_20171230.csv +[12/30/17 23:17:50] INFO Creating Cross-Tabulations +[12/30/17 23:17:50] INFO Original Features : Index(['Pclass', 'Name', 'Sex', 'Age', 'SibSp', 'Parch', 'Ticket', 'Fare', 'Cabin', 'Embarked'], dtype='object') -[04/18/17 12:08:34] INFO Feature Count : 10 -[04/18/17 12:08:34] INFO Creating Count Features -[04/18/17 12:08:34] INFO NA Counts -[04/18/17 12:08:34] INFO Number Counts -[04/18/17 12:08:34] INFO New Feature Count : 21 -[04/18/17 12:08:34] INFO Creating Base Features -[04/18/17 12:08:34] INFO Feature 1: Pclass is a numerical feature of type int64 with 3 unique values -[04/18/17 12:08:34] INFO Feature 2: Name is a text feature [12:82] with 1307 unique values -[04/18/17 12:08:34] INFO Feature 2: Name => Factorization -[04/18/17 12:08:34] INFO Feature 3: Sex is a text feature [4:6] with 2 unique values -[04/18/17 12:08:34] INFO Feature 3: Sex => Factorization -[04/18/17 12:08:34] INFO Feature 4: Age is a numerical feature of type float64 with 99 unique values -[04/18/17 12:08:34] INFO Feature 5: SibSp is a numerical feature of type int64 with 7 unique values -[04/18/17 12:08:34] INFO Feature 6: Parch is a numerical feature of type int64 with 8 unique values -[04/18/17 12:08:34] INFO Feature 7: Ticket is a text feature [3:18] with 929 unique values -[04/18/17 12:08:34] INFO Feature 7: Ticket => Factorization -[04/18/17 12:08:34] INFO Feature 8: Fare is a numerical feature of type float64 with 282 unique values -[04/18/17 12:08:34] INFO Feature 9: Cabin is a text feature [1:15] with 187 unique values -[04/18/17 12:08:34] INFO Feature 9: Cabin => Factorization -[04/18/17 12:08:34] INFO Feature 10: Embarked is a text feature [1:1] with 4 unique values -[04/18/17 12:08:34] INFO Feature 10: Embarked => Factorization -[04/18/17 12:08:34] INFO Feature 11: nan_count is a numerical feature of type int64 with 3 unique values -[04/18/17 12:08:34] INFO Feature 12: count_0 is a numerical feature of type int64 with 4 unique values -[04/18/17 12:08:34] INFO Feature 13: count_1 is a numerical feature of type int64 with 4 unique values -[04/18/17 12:08:34] INFO Feature 14: count_2 is a numerical feature of type int64 with 4 unique values -[04/18/17 12:08:34] INFO Feature 15: count_3 is a numerical feature of type int64 with 4 unique values -[04/18/17 12:08:34] INFO Feature 16: count_4 is a numerical feature of type int64 with 3 unique values -[04/18/17 12:08:34] INFO Feature 17: count_5 is a numerical feature of type int64 with 2 unique values -[04/18/17 12:08:34] INFO Feature 18: count_6 is a numerical feature of type int64 with 2 unique values -[04/18/17 12:08:34] INFO Feature 19: count_7 is a numerical feature of type int64 with 2 unique values -[04/18/17 12:08:34] INFO Feature 20: count_8 is a numerical feature of type int64 with 3 unique values -[04/18/17 12:08:34] INFO Feature 21: count_9 is a numerical feature of type int64 with 3 unique values -[04/18/17 12:08:34] INFO New Feature Count : 21 -[04/18/17 12:08:34] INFO Scaling Base Features -[04/18/17 12:08:34] INFO Creating NumPy Features -[04/18/17 12:08:34] INFO NumPy Feature: sum -[04/18/17 12:08:34] INFO NumPy Feature: mean -[04/18/17 12:08:34] INFO NumPy Feature: standard deviation -[04/18/17 12:08:34] INFO NumPy Feature: variance -[04/18/17 12:08:34] INFO NumPy Feature Count : 4 -[04/18/17 12:08:34] INFO New Feature Count : 25 -[04/18/17 12:08:34] INFO Creating Clustering Features -[04/18/17 12:08:34] INFO Cluster Minimum : 3 -[04/18/17 12:08:34] INFO Cluster Maximum : 30 -[04/18/17 12:08:34] INFO Cluster Increment : 3 -[04/18/17 12:08:34] INFO k = 3 -[04/18/17 12:08:34] INFO k = 6 -[04/18/17 12:08:34] INFO k = 9 -[04/18/17 12:08:34] INFO k = 12 -[04/18/17 12:08:34] INFO k = 15 -[04/18/17 12:08:34] INFO k = 18 -[04/18/17 12:08:34] INFO k = 21 -[04/18/17 12:08:34] INFO k = 24 -[04/18/17 12:08:34] INFO k = 27 -[04/18/17 12:08:34] INFO k = 30 -[04/18/17 12:08:35] INFO Clustering Feature Count : 10 -[04/18/17 12:08:35] INFO New Feature Count : 35 -[04/18/17 12:08:35] INFO Saving New Features in Model -[04/18/17 12:08:35] INFO Creating Interactions -[04/18/17 12:08:35] INFO Initial Feature Count : 35 -[04/18/17 12:08:35] INFO Generating Polynomial Features -[04/18/17 12:08:35] INFO Interaction Percentage : 10 -[04/18/17 12:08:35] INFO Polynomial Degree : 5 -[04/18/17 12:08:35] INFO Polynomial Feature Count : 15 -[04/18/17 12:08:35] INFO New Total Feature Count : 50 -[04/18/17 12:08:35] INFO Saving New Features in Model -[04/18/17 12:08:35] INFO Removing Low-Variance Features -[04/18/17 12:08:35] INFO Low-Variance Threshold : 0.10 -[04/18/17 12:08:35] INFO Original Feature Count : 50 -[04/18/17 12:08:35] INFO Reduced Feature Count : 50 -[04/18/17 12:08:35] INFO Saving New Features in Model -[04/18/17 12:08:35] INFO Skipping Shuffling -[04/18/17 12:08:35] INFO Skipping Sampling -[04/18/17 12:08:35] INFO Getting Class Weights -[04/18/17 12:08:35] INFO Class Weight for target Survived [1]: 1.605263 -[04/18/17 12:08:35] INFO Getting All Estimators -[04/18/17 12:08:35] INFO Algorithm Configuration -[04/18/17 12:08:35] INFO Selecting Models -[04/18/17 12:08:35] INFO Algorithm: RF -[04/18/17 12:08:35] INFO Fitting Initial Model -[04/18/17 12:08:35] INFO Recursive Feature Elimination with CV -[04/18/17 12:08:58] INFO RFECV took 23.22 seconds for step 3 and 3 folds -[04/18/17 12:08:58] INFO Algorithm: RF, Selected Features: 14, Ranking: [ 5 1 1 1 8 11 1 1 4 8 11 8 9 9 9 13 12 13 13 11 12 1 1 1 1 - 12 10 7 6 2 7 4 6 7 3 3 1 5 10 1 2 10 1 4 6 1 2 3 5 1] -[04/18/17 12:08:58] INFO Randomized Grid Search -[04/18/17 12:09:39] INFO Grid Search took 40.42 seconds for 50 candidate parameter settings. -[04/18/17 12:09:39] INFO Model with rank: 1 -[04/18/17 12:09:39] INFO Mean validation score: 0.862 (std: 0.022) -[04/18/17 12:09:39] INFO Parameters: {'est__n_estimators': 201, 'est__min_samples_split': 10, 'est__min_samples_leaf': 1, 'est__max_depth': 7, 'est__criterion': 'entropy', 'est__bootstrap': True} -[04/18/17 12:09:39] INFO Model with rank: 2 -[04/18/17 12:09:39] INFO Mean validation score: 0.860 (std: 0.019) -[04/18/17 12:09:39] INFO Parameters: {'est__n_estimators': 201, 'est__min_samples_split': 10, 'est__min_samples_leaf': 3, 'est__max_depth': 10, 'est__criterion': 'gini', 'est__bootstrap': True} -[04/18/17 12:09:39] INFO Model with rank: 3 -[04/18/17 12:09:39] INFO Mean validation score: 0.859 (std: 0.022) -[04/18/17 12:09:39] INFO Parameters: {'est__n_estimators': 201, 'est__min_samples_split': 2, 'est__min_samples_leaf': 3, 'est__max_depth': 5, 'est__criterion': 'gini', 'est__bootstrap': True} -[04/18/17 12:09:39] INFO Algorithm: RF, Best Score: 0.8617, Best Parameters: {'est__n_estimators': 201, 'est__min_samples_split': 10, 'est__min_samples_leaf': 1, 'est__max_depth': 7, 'est__criterion': 'entropy', 'est__bootstrap': True} -[04/18/17 12:09:39] INFO Final Model Predictions for RF -[04/18/17 12:09:39] INFO Skipping Calibration -[04/18/17 12:09:39] INFO Making Predictions -[04/18/17 12:09:39] INFO Predictions Complete -[04/18/17 12:09:39] INFO Algorithm: XGB -[04/18/17 12:09:39] INFO Fitting Initial Model -[04/18/17 12:09:39] INFO No RFE Available for XGB -[04/18/17 12:09:39] INFO Randomized Grid Search -[04/18/17 12:10:03] INFO Grid Search took 23.32 seconds for 50 candidate parameter settings. -[04/18/17 12:10:03] INFO Model with rank: 1 -[04/18/17 12:10:03] INFO Mean validation score: 0.857 (std: 0.016) -[04/18/17 12:10:03] INFO Parameters: {'est__subsample': 0.5, 'est__n_estimators': 51, 'est__min_child_weight': 1.1, 'est__max_depth': 9, 'est__learning_rate': 0.02, 'est__colsample_bytree': 0.8} -[04/18/17 12:10:03] INFO Model with rank: 2 -[04/18/17 12:10:03] INFO Mean validation score: 0.857 (std: 0.017) -[04/18/17 12:10:03] INFO Parameters: {'est__subsample': 0.5, 'est__n_estimators': 21, 'est__min_child_weight': 1.1, 'est__max_depth': 10, 'est__learning_rate': 0.01, 'est__colsample_bytree': 1.0} -[04/18/17 12:10:03] INFO Model with rank: 3 -[04/18/17 12:10:03] INFO Mean validation score: 0.857 (std: 0.016) -[04/18/17 12:10:03] INFO Parameters: {'est__subsample': 0.8, 'est__n_estimators': 51, 'est__min_child_weight': 1.1, 'est__max_depth': 5, 'est__learning_rate': 0.05, 'est__colsample_bytree': 0.9} -[04/18/17 12:10:03] INFO Algorithm: XGB, Best Score: 0.8574, Best Parameters: {'est__subsample': 0.5, 'est__n_estimators': 51, 'est__min_child_weight': 1.1, 'est__max_depth': 9, 'est__learning_rate': 0.02, 'est__colsample_bytree': 0.8} -[04/18/17 12:10:03] INFO Final Model Predictions for XGB -[04/18/17 12:10:03] INFO Skipping Calibration -[04/18/17 12:10:03] INFO Making Predictions -[04/18/17 12:10:03] INFO Predictions Complete -[04/18/17 12:10:03] INFO Blending Models -[04/18/17 12:10:03] INFO Blending Start: 2017-04-18 12:10:03.066771 -[04/18/17 12:10:03] INFO Blending Complete: 0:00:00.008102 -[04/18/17 12:10:03] INFO ================================================================================ -[04/18/17 12:10:03] INFO Metrics for: Partition.train -[04/18/17 12:10:03] INFO -------------------------------------------------------------------------------- -[04/18/17 12:10:03] INFO Algorithm: RF -[04/18/17 12:10:03] INFO accuracy: 0.894500561167 -[04/18/17 12:10:03] INFO adjusted_rand_score: 0.61985360714 -[04/18/17 12:10:03] INFO average_precision: 0.937994687253 -[04/18/17 12:10:03] INFO confusion_matrix: [[525 24] - [ 70 272]] -[04/18/17 12:10:03] INFO explained_variance: 0.565195624154 -[04/18/17 12:10:03] INFO f1: 0.852664576803 -[04/18/17 12:10:03] INFO mean_absolute_error: 0.105499438833 -[04/18/17 12:10:03] INFO median_absolute_error: 0.0 -[04/18/17 12:10:03] INFO neg_log_loss: 0.304153797611 -[04/18/17 12:10:03] INFO neg_mean_squared_error: 0.105499438833 -[04/18/17 12:10:03] INFO precision: 0.918918918919 -[04/18/17 12:10:03] INFO r2: 0.553925798102 -[04/18/17 12:10:03] INFO recall: 0.795321637427 -[04/18/17 12:10:03] INFO roc_auc: 0.953855494839 -[04/18/17 12:10:03] INFO -------------------------------------------------------------------------------- -[04/18/17 12:10:03] INFO Algorithm: XGB -[04/18/17 12:10:03] INFO accuracy: 0.89898989899 -[04/18/17 12:10:03] INFO adjusted_rand_score: 0.634084281404 -[04/18/17 12:10:03] INFO average_precision: 0.933465422975 -[04/18/17 12:10:03] INFO confusion_matrix: [[529 20] - [ 70 272]] -[04/18/17 12:10:03] INFO explained_variance: 0.586222690911 -[04/18/17 12:10:03] INFO f1: 0.858044164038 -[04/18/17 12:10:03] INFO mean_absolute_error: 0.10101010101 -[04/18/17 12:10:03] INFO median_absolute_error: 0.0 -[04/18/17 12:10:03] INFO neg_log_loss: 0.413047928351 -[04/18/17 12:10:03] INFO neg_mean_squared_error: 0.10101010101 -[04/18/17 12:10:03] INFO precision: 0.931506849315 -[04/18/17 12:10:03] INFO r2: 0.572907679034 -[04/18/17 12:10:03] INFO recall: 0.795321637427 -[04/18/17 12:10:03] INFO roc_auc: 0.950199192578 -[04/18/17 12:10:03] INFO ================================================================================ -[04/18/17 12:10:03] INFO Metrics for: Partition.test -[04/18/17 12:10:03] INFO No labels for generating Partition.test metrics -[04/18/17 12:10:03] INFO ================================================================================ -[04/18/17 12:10:03] INFO Selecting Best Model -[04/18/17 12:10:03] INFO Scoring for: Partition.train -[04/18/17 12:10:03] INFO Best Model Selection Start: 2017-04-18 12:10:03.106245 -[04/18/17 12:10:03] INFO Scoring RF Model -[04/18/17 12:10:03] INFO Scoring XGB Model -[04/18/17 12:10:03] INFO Scoring BLEND Model -[04/18/17 12:10:03] INFO Best Model is BLEND with a roc_auc score of 0.9539 -[04/18/17 12:10:03] INFO Best Model Selection Complete: 0:00:00.001146 -[04/18/17 12:10:03] INFO ================================================================================ -[04/18/17 12:10:03] INFO Generating Plots for partition: train -[04/18/17 12:10:03] INFO Generating Calibration Plot -[04/18/17 12:10:03] INFO Calibration for Algorithm: RF -[04/18/17 12:10:03] INFO Calibration for Algorithm: XGB -[04/18/17 12:10:03] INFO Writing plot to /Users/markconway/Projects/Titanic/plots/calibration_train.png -[04/18/17 12:10:03] INFO Generating Confusion Matrices -[04/18/17 12:10:03] INFO Confusion Matrix for Algorithm: RF -[04/18/17 12:10:03] INFO Confusion Matrix: -[04/18/17 12:10:03] INFO [[525 24] - [ 70 272]] -[04/18/17 12:10:03] INFO Writing plot to /Users/markconway/Projects/Titanic/plots/confusion_train_RF.png -[04/18/17 12:10:03] INFO Confusion Matrix for Algorithm: XGB -[04/18/17 12:10:03] INFO Confusion Matrix: -[04/18/17 12:10:03] INFO [[529 20] - [ 70 272]] -[04/18/17 12:10:03] INFO Writing plot to /Users/markconway/Projects/Titanic/plots/confusion_train_XGB.png -[04/18/17 12:10:04] INFO Generating ROC Curves -[04/18/17 12:10:04] INFO ROC Curve for Algorithm: RF -[04/18/17 12:10:04] INFO ROC Curve for Algorithm: XGB -[04/18/17 12:10:04] INFO Writing plot to /Users/markconway/Projects/Titanic/plots/roc_curve_train.png -[04/18/17 12:10:04] INFO Generating Learning Curves -[04/18/17 12:10:04] INFO Algorithm Configuration -[04/18/17 12:10:04] INFO Learning Curve for Algorithm: RF -[04/18/17 12:10:06] INFO Writing plot to /Users/markconway/Projects/Titanic/plots/learning_curve_train_RF.png -[04/18/17 12:10:06] INFO Learning Curve for Algorithm: XGB -[04/18/17 12:10:06] INFO Writing plot to /Users/markconway/Projects/Titanic/plots/learning_curve_train_XGB.png -[04/18/17 12:10:06] INFO Generating Feature Importance Plots -[04/18/17 12:10:06] INFO Feature Importances for Algorithm: RF -[04/18/17 12:10:06] INFO Feature Ranking: -[04/18/17 12:10:06] INFO 1. Feature 2 (0.108046) -[04/18/17 12:10:06] INFO 2. Feature 36 (0.073566) -[04/18/17 12:10:06] INFO 3. Feature 39 (0.057581) -[04/18/17 12:10:06] INFO 4. Feature 3 (0.051578) -[04/18/17 12:10:06] INFO 5. Feature 7 (0.046824) -[04/18/17 12:10:06] INFO 6. Feature 42 (0.046571) -[04/18/17 12:10:06] INFO 7. Feature 24 (0.045251) -[04/18/17 12:10:06] INFO 8. Feature 23 (0.044804) -[04/18/17 12:10:06] INFO 9. Feature 22 (0.038629) -[04/18/17 12:10:06] INFO 10. Feature 21 (0.037361) -[04/18/17 12:10:06] INFO Writing plot to /Users/markconway/Projects/Titanic/plots/feature_importance_train_RF.png -[04/18/17 12:10:07] INFO Feature Importances for Algorithm: XGB -[04/18/17 12:10:07] INFO Feature Ranking: -[04/18/17 12:10:07] INFO 1. Feature 2 (0.138577) -[04/18/17 12:10:07] INFO 2. Feature 3 (0.119850) -[04/18/17 12:10:07] INFO 3. Feature 0 (0.116105) -[04/18/17 12:10:07] INFO 4. Feature 7 (0.108614) -[04/18/17 12:10:07] INFO 5. Feature 31 (0.104869) -[04/18/17 12:10:07] INFO 6. Feature 23 (0.074906) -[04/18/17 12:10:07] INFO 7. Feature 40 (0.067416) -[04/18/17 12:10:07] INFO 8. Feature 4 (0.033708) -[04/18/17 12:10:07] INFO 9. Feature 33 (0.029963) -[04/18/17 12:10:07] INFO 10. Feature 9 (0.029963) -[04/18/17 12:10:07] INFO Writing plot to /Users/markconway/Projects/Titanic/plots/feature_importance_train_XGB.png -[04/18/17 12:10:07] INFO ================================================================================ -[04/18/17 12:10:07] INFO Saving Model Predictor -[04/18/17 12:10:07] INFO Writing model predictor to /Users/markconway/Projects/Titanic/model/model_20170418.pkl -[04/18/17 12:10:07] INFO Saving Feature Map -[04/18/17 12:10:07] INFO Writing feature map to /Users/markconway/Projects/Titanic/model/feature_map_20170418.pkl -[04/18/17 12:10:07] INFO Loading data from /Users/markconway/Projects/Titanic/input/test.csv -[04/18/17 12:10:07] INFO Saving Predictions -[04/18/17 12:10:07] INFO Storing output to /Users/markconway/Projects/Titanic/output/predictions_20170418.csv -[04/18/17 12:10:07] INFO Saving Probabilities -[04/18/17 12:10:07] INFO Storing output to /Users/markconway/Projects/Titanic/output/probabilities_20170418.csv -[04/18/17 12:10:07] INFO Saving Ranked Predictions -[04/18/17 12:10:07] INFO Writing data frame to /Users/markconway/Projects/Titanic/output/rankings_20170418.csv -[04/18/17 12:10:07] INFO Saving Submission -[04/18/17 12:10:07] INFO ******************************************************************************** -[04/18/17 12:10:07] INFO AlphaPy End -[04/18/17 12:10:07] INFO ******************************************************************************** +[12/30/17 23:17:50] INFO Feature Count : 10 +[12/30/17 23:17:50] INFO Creating Count Features +[12/30/17 23:17:50] INFO NA Counts +[12/30/17 23:17:50] INFO Number Counts +[12/30/17 23:17:50] INFO New Feature Count : 21 +[12/30/17 23:17:50] INFO Creating Base Features +[12/30/17 23:17:50] INFO Feature 1: Pclass is a numerical feature of type int64 with 3 unique values +[12/30/17 23:17:50] INFO Feature 2: Name is a text feature [12:82] with 1307 unique values +[12/30/17 23:17:50] INFO Feature 2: Name => Factorization +[12/30/17 23:17:50] INFO Feature 3: Sex is a text feature [4:6] with 2 unique values +[12/30/17 23:17:50] INFO Feature 3: Sex => Factorization +[12/30/17 23:17:50] INFO Feature 4: Age is a numerical feature of type float64 with 99 unique values +[12/30/17 23:17:50] INFO Feature 5: SibSp is a numerical feature of type int64 with 7 unique values +[12/30/17 23:17:50] INFO Feature 6: Parch is a numerical feature of type int64 with 8 unique values +[12/30/17 23:17:50] INFO Feature 7: Ticket is a text feature [3:18] with 929 unique values +[12/30/17 23:17:50] INFO Feature 7: Ticket => Factorization +[12/30/17 23:17:50] INFO Feature 8: Fare is a numerical feature of type float64 with 282 unique values +[12/30/17 23:17:50] INFO Feature 9: Cabin is a text feature [1:15] with 187 unique values +[12/30/17 23:17:50] INFO Feature 9: Cabin => Factorization +[12/30/17 23:17:50] INFO Feature 10: Embarked is a text feature [1:1] with 4 unique values +[12/30/17 23:17:50] INFO Feature 10: Embarked => Factorization +[12/30/17 23:17:50] INFO Feature 11: nan_count is a numerical feature of type int64 with 3 unique values +[12/30/17 23:17:50] INFO Feature 12: count_0 is a numerical feature of type int64 with 4 unique values +[12/30/17 23:17:50] INFO Feature 13: count_1 is a numerical feature of type int64 with 4 unique values +[12/30/17 23:17:50] INFO Feature 14: count_2 is a numerical feature of type int64 with 4 unique values +[12/30/17 23:17:50] INFO Feature 15: count_3 is a numerical feature of type int64 with 4 unique values +[12/30/17 23:17:50] INFO Feature 16: count_4 is a numerical feature of type int64 with 3 unique values +[12/30/17 23:17:50] INFO Feature 17: count_5 is a numerical feature of type int64 with 2 unique values +[12/30/17 23:17:50] INFO Feature 18: count_6 is a numerical feature of type int64 with 2 unique values +[12/30/17 23:17:50] INFO Feature 19: count_7 is a numerical feature of type int64 with 2 unique values +[12/30/17 23:17:50] INFO Feature 20: count_8 is a numerical feature of type int64 with 3 unique values +[12/30/17 23:17:50] INFO Feature 21: count_9 is a numerical feature of type int64 with 3 unique values +[12/30/17 23:17:50] INFO New Feature Count : 21 +[12/30/17 23:17:50] INFO Scaling Base Features +[12/30/17 23:17:50] INFO Creating NumPy Features +[12/30/17 23:17:50] INFO NumPy Feature: sum +[12/30/17 23:17:50] INFO NumPy Feature: mean +[12/30/17 23:17:50] INFO NumPy Feature: standard deviation +[12/30/17 23:17:50] INFO NumPy Feature: variance +[12/30/17 23:17:50] INFO NumPy Feature Count : 4 +[12/30/17 23:17:50] INFO New Feature Count : 25 +[12/30/17 23:17:50] INFO Creating Clustering Features +[12/30/17 23:17:50] INFO Cluster Minimum : 3 +[12/30/17 23:17:50] INFO Cluster Maximum : 30 +[12/30/17 23:17:50] INFO Cluster Increment : 3 +[12/30/17 23:17:50] INFO k = 3 +[12/30/17 23:17:50] INFO k = 6 +[12/30/17 23:17:50] INFO k = 9 +[12/30/17 23:17:50] INFO k = 12 +[12/30/17 23:17:50] INFO k = 15 +[12/30/17 23:17:50] INFO k = 18 +[12/30/17 23:17:50] INFO k = 21 +[12/30/17 23:17:50] INFO k = 24 +[12/30/17 23:17:50] INFO k = 27 +[12/30/17 23:17:51] INFO k = 30 +[12/30/17 23:17:51] INFO Clustering Feature Count : 10 +[12/30/17 23:17:51] INFO New Feature Count : 35 +[12/30/17 23:17:51] INFO Saving New Features in Model +[12/30/17 23:17:51] INFO Creating Interactions +[12/30/17 23:17:51] INFO Initial Feature Count : 35 +[12/30/17 23:17:51] INFO Generating Polynomial Features +[12/30/17 23:17:51] INFO Interaction Percentage : 10 +[12/30/17 23:17:51] INFO Polynomial Degree : 5 +[12/30/17 23:17:51] INFO Polynomial Feature Count : 15 +[12/30/17 23:17:51] INFO New Total Feature Count : 50 +[12/30/17 23:17:51] INFO Saving New Features in Model +[12/30/17 23:17:51] INFO Removing Low-Variance Features +[12/30/17 23:17:51] INFO Low-Variance Threshold : 0.10 +[12/30/17 23:17:51] INFO Original Feature Count : 50 +[12/30/17 23:17:51] INFO Reduced Feature Count : 50 +[12/30/17 23:17:51] INFO Saving New Features in Model +[12/30/17 23:17:51] INFO Skipping Shuffling +[12/30/17 23:17:51] INFO Skipping Sampling +[12/30/17 23:17:51] INFO Getting Class Weights +[12/30/17 23:17:51] INFO Class Weight for target Survived [1]: 1.605263 +[12/30/17 23:17:51] INFO Getting All Estimators +[12/30/17 23:17:51] INFO Algorithm Configuration +[12/30/17 23:17:51] INFO Selecting Models +[12/30/17 23:17:51] INFO Algorithm: RF +[12/30/17 23:17:51] INFO Fitting Initial Model +[12/30/17 23:17:51] INFO Recursive Feature Elimination with CV +[12/30/17 23:18:14] INFO RFECV took 22.72 seconds for step 3 and 3 folds +[12/30/17 23:18:14] INFO Algorithm: RF, Selected Features: 20, Ranking: [ 2 1 1 1 5 9 1 1 2 6 8 7 8 6 7 10 11 11 11 10 10 1 1 1 1 + 9 6 9 5 1 5 4 2 4 1 1 1 3 7 1 1 8 1 1 4 1 1 3 3 1] +[12/30/17 23:18:14] INFO Randomized Grid Search +[12/30/17 23:19:08] INFO Grid Search took 54.03 seconds for 50 candidate parameter settings. +[12/30/17 23:19:08] INFO Model with rank: 1 +[12/30/17 23:19:08] INFO Mean validation score: 0.863 (std: 0.014) +[12/30/17 23:19:08] INFO Parameters: {'est__n_estimators': 501, 'est__min_samples_split': 5, 'est__min_samples_leaf': 3, 'est__max_depth': 7, 'est__criterion': 'entropy', 'est__bootstrap': True} +[12/30/17 23:19:08] INFO Model with rank: 2 +[12/30/17 23:19:08] INFO Mean validation score: 0.862 (std: 0.015) +[12/30/17 23:19:08] INFO Parameters: {'est__n_estimators': 201, 'est__min_samples_split': 10, 'est__min_samples_leaf': 2, 'est__max_depth': 7, 'est__criterion': 'entropy', 'est__bootstrap': True} +[12/30/17 23:19:08] INFO Model with rank: 3 +[12/30/17 23:19:08] INFO Mean validation score: 0.861 (std: 0.014) +[12/30/17 23:19:08] INFO Parameters: {'est__n_estimators': 101, 'est__min_samples_split': 2, 'est__min_samples_leaf': 3, 'est__max_depth': 7, 'est__criterion': 'entropy', 'est__bootstrap': True} +[12/30/17 23:19:08] INFO Algorithm: RF, Best Score: 0.8627, Best Parameters: {'est__n_estimators': 501, 'est__min_samples_split': 5, 'est__min_samples_leaf': 3, 'est__max_depth': 7, 'est__criterion': 'entropy', 'est__bootstrap': True} +[12/30/17 23:19:08] INFO Final Model Predictions for RF +[12/30/17 23:19:08] INFO Skipping Calibration +[12/30/17 23:19:08] INFO Making Predictions +[12/30/17 23:19:09] INFO Predictions Complete +[12/30/17 23:19:09] INFO Algorithm: XGB +[12/30/17 23:19:09] INFO Fitting Initial Model +[12/30/17 23:19:09] INFO No RFE Available for XGB +[12/30/17 23:19:09] INFO Randomized Grid Search +[12/30/17 23:19:32] INFO Grid Search took 23.44 seconds for 50 candidate parameter settings. +[12/30/17 23:19:32] INFO Model with rank: 1 +[12/30/17 23:19:32] INFO Mean validation score: 0.863 (std: 0.020) +[12/30/17 23:19:32] INFO Parameters: {'est__subsample': 0.6, 'est__n_estimators': 21, 'est__min_child_weight': 1.1, 'est__max_depth': 12, 'est__learning_rate': 0.1, 'est__colsample_bytree': 0.7} +[12/30/17 23:19:32] INFO Model with rank: 2 +[12/30/17 23:19:32] INFO Mean validation score: 0.856 (std: 0.014) +[12/30/17 23:19:32] INFO Parameters: {'est__subsample': 0.5, 'est__n_estimators': 51, 'est__min_child_weight': 1.0, 'est__max_depth': 8, 'est__learning_rate': 0.01, 'est__colsample_bytree': 0.7} +[12/30/17 23:19:32] INFO Model with rank: 3 +[12/30/17 23:19:32] INFO Mean validation score: 0.855 (std: 0.023) +[12/30/17 23:19:32] INFO Parameters: {'est__subsample': 0.5, 'est__n_estimators': 21, 'est__min_child_weight': 1.0, 'est__max_depth': 7, 'est__learning_rate': 0.05, 'est__colsample_bytree': 0.6} +[12/30/17 23:19:32] INFO Algorithm: XGB, Best Score: 0.8627, Best Parameters: {'est__subsample': 0.6, 'est__n_estimators': 21, 'est__min_child_weight': 1.1, 'est__max_depth': 12, 'est__learning_rate': 0.1, 'est__colsample_bytree': 0.7} +[12/30/17 23:19:32] INFO Final Model Predictions for XGB +[12/30/17 23:19:32] INFO Skipping Calibration +[12/30/17 23:19:32] INFO Making Predictions +[12/30/17 23:19:32] INFO Predictions Complete +[12/30/17 23:19:32] INFO Blending Models +[12/30/17 23:19:32] INFO Blending Start: 2017-12-30 23:19:32.734086 +[12/30/17 23:19:32] INFO Blending Complete: 0:00:00.010781 +[12/30/17 23:19:32] INFO ================================================================================ +[12/30/17 23:19:32] INFO Metrics for: Partition.train +[12/30/17 23:19:32] INFO -------------------------------------------------------------------------------- +[12/30/17 23:19:32] INFO Algorithm: RF +[12/30/17 23:19:32] INFO accuracy: 0.895622895623 +[12/30/17 23:19:32] INFO adjusted_rand_score: 0.623145355109 +[12/30/17 23:19:32] INFO average_precision: 0.939127507197 +[12/30/17 23:19:32] INFO confusion_matrix: [[530 19] + [ 74 268]] +[12/30/17 23:19:32] INFO explained_variance: 0.574782432706 +[12/30/17 23:19:32] INFO f1: 0.852146263911 +[12/30/17 23:19:32] INFO mean_absolute_error: 0.104377104377 +[12/30/17 23:19:32] INFO median_absolute_error: 0.0 +[12/30/17 23:19:32] INFO neg_log_loss: 0.299193724458 +[12/30/17 23:19:32] INFO neg_mean_squared_error: 0.104377104377 +[12/30/17 23:19:32] INFO precision: 0.933797909408 +[12/30/17 23:19:32] INFO r2: 0.558671268335 +[12/30/17 23:19:32] INFO recall: 0.783625730994 +[12/30/17 23:19:32] INFO roc_auc: 0.954665047561 +[12/30/17 23:19:32] INFO -------------------------------------------------------------------------------- +[12/30/17 23:19:32] INFO Algorithm: XGB +[12/30/17 23:19:32] INFO accuracy: 0.915824915825 +[12/30/17 23:19:32] INFO adjusted_rand_score: 0.689583531462 +[12/30/17 23:19:32] INFO average_precision: 0.958117084885 +[12/30/17 23:19:32] INFO confusion_matrix: [[532 17] + [ 58 284]] +[12/30/17 23:19:32] INFO explained_variance: 0.653042746514 +[12/30/17 23:19:32] INFO f1: 0.883359253499 +[12/30/17 23:19:32] INFO mean_absolute_error: 0.0841750841751 +[12/30/17 23:19:32] INFO median_absolute_error: 0.0 +[12/30/17 23:19:32] INFO neg_log_loss: 0.295991903662 +[12/30/17 23:19:32] INFO neg_mean_squared_error: 0.0841750841751 +[12/30/17 23:19:32] INFO precision: 0.943521594684 +[12/30/17 23:19:32] INFO r2: 0.644089732528 +[12/30/17 23:19:32] INFO recall: 0.830409356725 +[12/30/17 23:19:32] INFO roc_auc: 0.971127728246 +[12/30/17 23:19:32] INFO ================================================================================ +[12/30/17 23:19:32] INFO Metrics for: Partition.test +[12/30/17 23:19:32] INFO No labels for generating Partition.test metrics +[12/30/17 23:19:32] INFO ================================================================================ +[12/30/17 23:19:32] INFO Selecting Best Model +[12/30/17 23:19:32] INFO Scoring for: Partition.train +[12/30/17 23:19:32] INFO Best Model Selection Start: 2017-12-30 23:19:32.779281 +[12/30/17 23:19:32] INFO Scoring RF Model +[12/30/17 23:19:32] INFO Scoring XGB Model +[12/30/17 23:19:32] INFO Scoring BLEND Model +[12/30/17 23:19:32] INFO Best Model is XGB with a roc_auc score of 0.9711 +[12/30/17 23:19:32] INFO Best Model Selection Complete: 0:00:00.000801 +[12/30/17 23:19:32] INFO ================================================================================ +[12/30/17 23:19:32] INFO Generating Plots for partition: train +[12/30/17 23:19:32] INFO Generating Calibration Plot +[12/30/17 23:19:32] INFO Calibration for Algorithm: RF +[12/30/17 23:19:32] INFO Calibration for Algorithm: XGB +[12/30/17 23:19:32] INFO Writing plot to ./plots/calibration_train.png +[12/30/17 23:19:33] INFO Generating Confusion Matrices +[12/30/17 23:19:33] INFO Confusion Matrix for Algorithm: RF +[12/30/17 23:19:33] INFO Confusion Matrix: +[12/30/17 23:19:33] INFO [[530 19] + [ 74 268]] +[12/30/17 23:19:33] INFO Writing plot to ./plots/confusion_train_RF.png +[12/30/17 23:19:33] INFO Confusion Matrix for Algorithm: XGB +[12/30/17 23:19:33] INFO Confusion Matrix: +[12/30/17 23:19:33] INFO [[532 17] + [ 58 284]] +[12/30/17 23:19:33] INFO Writing plot to ./plots/confusion_train_XGB.png +[12/30/17 23:19:33] INFO Generating ROC Curves +[12/30/17 23:19:33] INFO ROC Curve for Algorithm: RF +[12/30/17 23:19:33] INFO ROC Curve for Algorithm: XGB +[12/30/17 23:19:33] INFO Writing plot to ./plots/roc_curve_train.png +[12/30/17 23:19:33] INFO Generating Learning Curves +[12/30/17 23:19:33] INFO Algorithm Configuration +[12/30/17 23:19:33] INFO Learning Curve for Algorithm: RF +[12/30/17 23:19:35] INFO Writing plot to ./plots/learning_curve_train_RF.png +[12/30/17 23:19:35] INFO Learning Curve for Algorithm: XGB +[12/30/17 23:19:36] INFO Writing plot to ./plots/learning_curve_train_XGB.png +[12/30/17 23:19:36] INFO Generating Feature Importance Plots +[12/30/17 23:19:36] INFO Feature Importances for Algorithm: RF +[12/30/17 23:19:36] INFO Feature Ranking: +[12/30/17 23:19:36] INFO 1. Feature 2 (0.106345) +[12/30/17 23:19:36] INFO 2. Feature 36 (0.074083) +[12/30/17 23:19:36] INFO 3. Feature 39 (0.055330) +[12/30/17 23:19:36] INFO 4. Feature 3 (0.050339) +[12/30/17 23:19:36] INFO 5. Feature 7 (0.049228) +[12/30/17 23:19:36] INFO 6. Feature 23 (0.044868) +[12/30/17 23:19:36] INFO 7. Feature 22 (0.042925) +[12/30/17 23:19:36] INFO 8. Feature 42 (0.042850) +[12/30/17 23:19:36] INFO 9. Feature 21 (0.039954) +[12/30/17 23:19:36] INFO 10. Feature 24 (0.038563) +[12/30/17 23:19:36] INFO Writing plot to ./plots/feature_importance_train_RF.png +[12/30/17 23:19:36] INFO Feature Importances for Algorithm: XGB +[12/30/17 23:19:36] INFO Feature Ranking: +[12/30/17 23:19:36] INFO 1. Feature 2 (0.142857) +[12/30/17 23:19:36] INFO 2. Feature 3 (0.120301) +[12/30/17 23:19:36] INFO 3. Feature 0 (0.116541) +[12/30/17 23:19:36] INFO 4. Feature 7 (0.109023) +[12/30/17 23:19:36] INFO 5. Feature 31 (0.105263) +[12/30/17 23:19:36] INFO 6. Feature 23 (0.075188) +[12/30/17 23:19:36] INFO 7. Feature 40 (0.071429) +[12/30/17 23:19:36] INFO 8. Feature 8 (0.033835) +[12/30/17 23:19:36] INFO 9. Feature 4 (0.030075) +[12/30/17 23:19:36] INFO 10. Feature 9 (0.030075) +[12/30/17 23:19:36] INFO Writing plot to ./plots/feature_importance_train_XGB.png +[12/30/17 23:19:36] INFO ================================================================================ +[12/30/17 23:19:36] INFO Saving Model Predictor +[12/30/17 23:19:36] INFO Writing model predictor to ./model/model_20171230.pkl +[12/30/17 23:19:36] INFO Saving Feature Map +[12/30/17 23:19:36] INFO Writing feature map to ./model/feature_map_20171230.pkl +[12/30/17 23:19:36] INFO Loading data from ./input/test.csv +[12/30/17 23:19:36] INFO Saving Predictions +[12/30/17 23:19:36] INFO Storing output to ./output/predictions_20171230.csv +[12/30/17 23:19:36] INFO Saving Probabilities +[12/30/17 23:19:36] INFO Storing output to ./output/probabilities_20171230.csv +[12/30/17 23:19:36] INFO Saving Ranked Predictions +[12/30/17 23:19:36] INFO Writing data frame to ./output/rankings_20171230.csv +[12/30/17 23:19:36] INFO Saving Submission to ./output/submission_20171230.csv +[12/30/17 23:19:36] INFO ******************************************************************************** +[12/30/17 23:19:36] INFO AlphaPy End +[12/30/17 23:19:36] INFO ******************************************************************************** diff --git a/setup.py b/setup.py index 94ef85a..979d183 100644 --- a/setup.py +++ b/setup.py @@ -9,8 +9,8 @@ MAINTAINER = 'ScottFree LLC [Robert D. Scott II, Mark Conway]' MAINTAINER_EMAIL = 'scottfree.analytics@scottfreellc.com' URL = "https://github.com/ScottFreeLLC/AlphaPy" -LICENSE = "Apache License, Version 2.1" -VERSION = "2.1" +LICENSE = "Apache License, Version 2.2" +VERSION = "2.2" classifiers = ['Development Status :: 4 - Beta', 'Programming Language :: Python', From b4d1d56b60bbaa65073d4f7120e24d6c2961c120 Mon Sep 17 00:00:00 2001 From: Mark Conway Date: Sun, 31 Dec 2017 16:34:52 -0500 Subject: [PATCH 011/129] update version to 2.2.1 update version to 2.2.1 --- setup.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/setup.py b/setup.py index 979d183..7a5d4a2 100644 --- a/setup.py +++ b/setup.py @@ -9,8 +9,8 @@ MAINTAINER = 'ScottFree LLC [Robert D. Scott II, Mark Conway]' MAINTAINER_EMAIL = 'scottfree.analytics@scottfreellc.com' URL = "https://github.com/ScottFreeLLC/AlphaPy" -LICENSE = "Apache License, Version 2.2" -VERSION = "2.2" +LICENSE = "Apache License, Version 2" +VERSION = "2.2.1" classifiers = ['Development Status :: 4 - Beta', 'Programming Language :: Python', From 61ded5a18d28f8053d7547172fb2851b645c3dc4 Mon Sep 17 00:00:00 2001 From: Mark Conway Date: Thu, 25 Jan 2018 18:14:54 -0500 Subject: [PATCH 012/129] unify models and systems Use model output as system input. Apply probabilities generated by models to dictate trading rules. --- alphapy/__main__.py | 1 - alphapy/data.py | 115 +++++--- .../examples/Trading Model/config/market.yml | 3 +- .../examples/Trading System/config/market.yml | 3 +- alphapy/frame.py | 7 +- alphapy/market_flow.py | 104 ++++---- alphapy/market_variables.py | 3 +- alphapy/model.py | 40 ++- alphapy/optimize.py | 3 +- alphapy/system.py | 248 +++++++----------- alphapy/utilities.py | 30 +++ docs/tutorials/closer_market.yml | 3 +- docs/tutorials/rrover_market.yml | 3 +- 13 files changed, 298 insertions(+), 265 deletions(-) diff --git a/alphapy/__main__.py b/alphapy/__main__.py index ef5fa4b..973a0b0 100644 --- a/alphapy/__main__.py +++ b/alphapy/__main__.py @@ -61,7 +61,6 @@ from alphapy.optimize import rfecv_search from alphapy.plots import generate_plots from alphapy.utilities import get_datestamp -from alphapy.utilities import np_store_data import argparse from datetime import datetime diff --git a/alphapy/data.py b/alphapy/data.py index d15f658..adfab5c 100644 --- a/alphapy/data.py +++ b/alphapy/data.py @@ -31,11 +31,11 @@ from alphapy.frame import read_frame from alphapy.globals import ModelType from alphapy.globals import Partition, datasets -from alphapy.globals import PD_INTRADAY_OFFSETS from alphapy.globals import PD_WEB_DATA_FEEDS from alphapy.globals import PSEP, SSEP, USEP from alphapy.globals import SamplingMethod from alphapy.globals import WILDCARD +from alphapy.space import Space from datetime import datetime from datetime import timedelta @@ -284,6 +284,57 @@ def sample_data(model): return model +# +# Function convert_data +# + +def convert_data(df, index_column, intraday_data): + r"""Convert the market data frame to canonical format. + + Parameters + ---------- + df : pandas.DataFrame + The intraday dataframe. + index_column : str + The name of the index column. + intraday_data : bool + Flag set to True if the frame contains intraday data. + + Returns + ------- + df : pandas.DataFrame + The canonical dataframe with date/time index. + + """ + + # Standardize column names + df = df.rename(columns = lambda x: x.lower().replace(' ','')) + + # Create the time/date index if not already done + + if not isinstance(df.index, pd.DatetimeIndex): + if intraday_data: + dt_column = df['date'] + ' ' + df['time'] + else: + dt_column = df['date'] + df[index_column] = pd.to_datetime(dt_column) + df.set_index(pd.DatetimeIndex(df[index_column]), + drop=True, inplace=True) + del df['date'] + if intraday_data: + del df['time'] + + # Make the remaining columns floating point + + cols_float = ['open', 'high', 'low', 'close', 'volume'] + df[cols_float] = df[cols_float].astype(float) + + # Order the frame by increasing date if necessary + df = df.sort_index() + + return df + + # # Function enhance_intraday_data # @@ -303,17 +354,12 @@ def enhance_intraday_data(df): """ - # Convert the columns to proper data types + # Group by date first - index_column = 'datetime' - dt_column = df['date'] + ' ' + df['time'] - df[index_column] = pd.to_datetime(dt_column) - cols_float = ['open', 'high', 'low', 'close', 'volume'] - df[cols_float] = df[cols_float].astype(float) + df['date'] = df.index.strftime('%Y-%m-%d') + date_group = df.groupby('date') # Number the intraday bars - - date_group = df.groupby('date') df['bar_number'] = date_group.cumcount() # Mark the end of the trading day @@ -321,13 +367,9 @@ def enhance_intraday_data(df): df['end_of_day'] = False df.loc[date_group.tail(1).index, 'end_of_day'] = True - # Set the data frame's index - df.set_index(pd.DatetimeIndex(df[index_column]), drop=True, inplace=True) - # Return the enhanced frame del df['date'] - del df['time'] return df @@ -427,13 +469,13 @@ def get_pandas_data(schema, symbol, lookback_period): """ - # Quandl is a special case. + # Quandl is a special case with subfeeds. if 'quandl' in schema: schema, symbol_prefix = schema.split(USEP) symbol = SSEP.join([symbol_prefix, symbol]).upper() - # Calculate the start and end date for Yahoo. + # Calculate the start and end date. start = datetime.now() - timedelta(lookback_period) end = datetime.now() @@ -443,7 +485,6 @@ def get_pandas_data(schema, symbol, lookback_period): try: df = web.DataReader(symbol, schema, start, end) except: - df = None logger.info("Could not retrieve data for: %s", symbol) return df @@ -453,7 +494,8 @@ def get_pandas_data(schema, symbol, lookback_period): # Function get_market_data # -def get_market_data(model, group, lookback_period, resample_data): +def get_market_data(model, group, lookback_period, + data_fractal, intraday_data=False): r"""Get data from an external feed. Parameters @@ -464,6 +506,10 @@ def get_market_data(model, group, lookback_period, resample_data): The group of symbols. lookback_period : int The number of periods of data to retrieve. + data_fractal : str + Pandas offset alias. + intraday_data : bool + If True, then get intraday data. Returns ------- @@ -486,15 +532,13 @@ def get_market_data(model, group, lookback_period, resample_data): # Determine the feed source - if any(substring in fractal for substring in PD_INTRADAY_OFFSETS): + if intraday_data: # intraday data (date and time) - logger.info("Getting Intraday Data [%s] from %s", fractal, schema) - intraday_data = True + logger.info("Getting Intraday Data [%s] from %s", data_fractal, schema) index_column = 'datetime' else: # daily data or higher (date only) - logger.info("Getting Daily Data [%s] from %s", fractal, schema) - intraday_data = False + logger.info("Getting Daily Data [%s] from %s", data_fractal, schema) index_column = 'date' # Get the data from the relevant feed @@ -502,41 +546,42 @@ def get_market_data(model, group, lookback_period, resample_data): data_dir = SSEP.join([directory, 'data']) pandas_data = any(substring in schema for substring in PD_WEB_DATA_FEEDS) n_periods = 0 + resample_data = True if fractal != data_fractal else False for item in group.members: logger.info("Getting %s data for last %d days", item, lookback_period) # Locate the data source if schema == 'data': - fname = frame_name(item.lower(), gspace) + # local intraday or daily + dspace = Space(gspace.subject, gspace.schema, data_fractal) + fname = frame_name(item.lower(), dspace) df = read_frame(data_dir, fname, extension, separator) - if not intraday_data: - df.set_index(pd.DatetimeIndex(df[index_column]), - drop=True, inplace=True) elif schema == 'google' and intraday_data: - df = get_google_data(item, lookback_period, fractal) + # intraday only + df = get_google_data(item, lookback_period, data_fractal) elif pandas_data: + # daily only df = get_pandas_data(schema, item, lookback_period) else: logger.error("Unsupported Data Source: %s", schema) # Now that we have content, standardize the data if df is not None and not df.empty: logger.info("Rows: %d", len(df)) - # standardize column names - df = df.rename(columns = lambda x: x.lower().replace(' ','')) - # add intraday columns if necessary - if intraday_data: - df = enhance_intraday_data(df) - # order by increasing date if necessary - df = df.sort_index() - # resample data + # convert data to canonical form + df = convert_data(df, index_column, intraday_data) + # resample data and forward fill any NA values if resample_data: df = df.resample(fractal).agg({'open' : 'first', 'high' : 'max', 'low' : 'min', 'close' : 'last', 'volume' : 'sum'}) + df.dropna(axis=0, how='any', inplace=True) logger.info("Rows after Resampling at %s: %d", fractal, len(df)) + # add intraday columns if necessary + if intraday_data: + df = enhance_intraday_data(df) # allocate global Frame newf = Frame(item.lower(), gspace, df) if newf is None: diff --git a/alphapy/examples/Trading Model/config/market.yml b/alphapy/examples/Trading Model/config/market.yml index 3ed085e..4d9ed20 100644 --- a/alphapy/examples/Trading Model/config/market.yml +++ b/alphapy/examples/Trading Model/config/market.yml @@ -1,11 +1,12 @@ market: + create_model : True + data_fractal : 1d data_history : 500 forecast_period : 1 fractal : 1d lag_period : 1 leaders : ['gap', 'gapbadown', 'gapbaup', 'gapdown', 'gapup'] predict_history : 100 - resample_data : False schema : yahoo subject : stock target_group : test diff --git a/alphapy/examples/Trading System/config/market.yml b/alphapy/examples/Trading System/config/market.yml index d01f3bd..76debb1 100644 --- a/alphapy/examples/Trading System/config/market.yml +++ b/alphapy/examples/Trading System/config/market.yml @@ -1,11 +1,12 @@ market: + create_model : False + data_fractal : 1d data_history : 500 forecast_period : 1 fractal : 1d lag_period : 1 leaders : [] predict_history : 50 - resample_data : False schema : quandl_wiki subject : stock target_group : faang diff --git a/alphapy/frame.py b/alphapy/frame.py index aed19da..129241c 100644 --- a/alphapy/frame.py +++ b/alphapy/frame.py @@ -175,7 +175,7 @@ def read_frame(directory, filename, extension, separator, # def write_frame(df, directory, filename, extension, separator, - index=False, index_label=None): + index=False, index_label=None, columns=None): r"""Write a dataframe into a delimiter-separated file. Parameters @@ -194,6 +194,8 @@ def write_frame(df, directory, filename, extension, separator, If ``True``, write the row names (index). index_label : str, optional A column label for the ``index``. + columns : str, optional + A list of column names. Returns ------- @@ -204,7 +206,8 @@ def write_frame(df, directory, filename, extension, separator, file_all = SSEP.join([directory, file_only]) logger.info("Writing data frame to %s", file_all) try: - df.to_csv(file_all, sep=separator, index=index, index_label=index_label) + df.to_csv(file_all, sep=separator, index=index, + index_label=index_label, columns=columns) except: logger.info("Could not write data frame to %s", file_all) diff --git a/alphapy/market_flow.py b/alphapy/market_flow.py index 58c8f49..bf1b03a 100644 --- a/alphapy/market_flow.py +++ b/alphapy/market_flow.py @@ -30,6 +30,7 @@ from alphapy.analysis import Analysis from alphapy.analysis import run_analysis from alphapy.data import get_market_data +from alphapy.globals import PD_INTRADAY_OFFSETS from alphapy.globals import PSEP, SSEP from alphapy.group import Group from alphapy.market_variables import Variable @@ -89,19 +90,26 @@ def get_market_config(): # Section: market [this section must be first] + specs['create_model'] = cfg['market']['create_model'] + fractal = cfg['market']['data_fractal'] + try: + test_interval = pd.to_timedelta(fractal) + except: + logger.info("data_fractal [%s] is an invalid pandas offset", + fractal) + specs['data_fractal'] = fractal + specs['data_history'] = cfg['market']['data_history'] specs['forecast_period'] = cfg['market']['forecast_period'] fractal = cfg['market']['fractal'] try: test_interval = pd.to_timedelta(fractal) except: - logger.info("Pandas offset alias [%s] is invalid for resampling", + logger.info("fractal [%s] is an invalid pandas offset", fractal) specs['fractal'] = fractal specs['lag_period'] = cfg['market']['lag_period'] specs['leaders'] = cfg['market']['leaders'] - specs['data_history'] = cfg['market']['data_history'] specs['predict_history'] = cfg['market']['predict_history'] - specs['resample_data'] = cfg['market']['resample_data'] specs['schema'] = cfg['market']['schema'] specs['subject'] = cfg['market']['subject'] specs['target_group'] = cfg['market']['target_group'] @@ -150,8 +158,13 @@ def get_market_config(): # Section: variables + logger.info("Defining AlphaPy Variables [phigh, plow]") + + Variable('phigh', 'probability >= 0.7') + Variable('plow', 'probability <= 0.3') + try: - logger.info("Defining Variables") + logger.info("Defining User Variables") for k, v in list(cfg['variables'].items()): Variable(k, v) except: @@ -169,6 +182,8 @@ def get_market_config(): # Log the stock parameters logger.info('MARKET PARAMETERS:') + logger.info('create_model = %r', specs['create_model']) + logger.info('data_fractal = %s', specs['data_fractal']) logger.info('data_history = %d', specs['data_history']) logger.info('features = %s', specs['features']) logger.info('forecast_period = %d', specs['forecast_period']) @@ -176,7 +191,6 @@ def get_market_config(): logger.info('lag_period = %d', specs['lag_period']) logger.info('leaders = %s', specs['leaders']) logger.info('predict_history = %s', specs['predict_history']) - logger.info('resample_data = %r', specs['resample_data']) logger.info('schema = %s', specs['schema']) logger.info('subject = %s', specs['subject']) logger.info('system = %s', specs['system']) @@ -217,80 +231,82 @@ def market_pipeline(model, market_specs): logger.info("Running MarketFlow Pipeline") - # Get any model specifications + # Get model specifications predict_mode = model.specs['predict_mode'] target = model.specs['target'] - # Get any market specifications + # Get market specifications + create_model = market_specs['create_model'] + data_fractal = market_specs['data_fractal'] data_history = market_specs['data_history'] features = market_specs['features'] forecast_period = market_specs['forecast_period'] + fractal = market_specs['fractal'] functions = market_specs['functions'] lag_period = market_specs['lag_period'] leaders = market_specs['leaders'] predict_history = market_specs['predict_history'] - resample_data = market_specs['resample_data'] target_group = market_specs['target_group'] - # Get the system specifications - - system_specs = market_specs['system'] - if system_specs: - system_name = system_specs['name'] - try: - longshort = True - longentry = system_specs['longentry'] - shortentry = system_specs['shortentry'] - longexit = system_specs['longexit'] - shortexit = system_specs['shortexit'] - holdperiod = system_specs['holdperiod'] - scale = system_specs['scale'] - logger.info("Running Long/Short System %s", system_name) - except: - longshort = False - system_params = system_specs['params'] - logger.info("Running System %s", system_name) - # Set the target group group = Group.groups[target_group] - logger.info("All Members: %s", group.members) + logger.info("All Symbols: %s", group.members) + + # Determine whether or not this is an intraday analysis. + + intraday = any(substring in fractal for substring in PD_INTRADAY_OFFSETS) # Get stock data. If we can't get all the data, then # predict_history resets to the actual history obtained. lookback = predict_history if predict_mode else data_history - new_history = get_market_data(model, group, lookback, resample_data) + new_history = get_market_data(model, group, lookback, + data_fractal, intraday) if new_history < data_history: logger.info("Maximum Data History is %d, not %d", new_history, data_history) if new_history == 0: raise ValueError("Could not get market data from source") - # Apply the features to all of the frames + # Run an analysis to create the model - vmapply(group, features, functions) - vmapply(group, [target], functions) + if create_model: + # apply features to all of the frames + vmapply(group, features, functions) + vmapply(group, [target], functions) + # run the analysis, including the model pipeline + a = Analysis(model, group) + results = run_analysis(a, lag_period, forecast_period, + leaders, predict_history) - # Run a system or an analysis + # Run a system + system_specs = market_specs['system'] if system_specs: + # get the system specs + system_name = system_specs['name'] + longentry = system_specs['longentry'] + shortentry = system_specs['shortentry'] + longexit = system_specs['longexit'] + shortexit = system_specs['shortexit'] + holdperiod = system_specs['holdperiod'] + scale = system_specs['scale'] + logger.info("Running System %s", system_name) + logger.info("Long Entry : %s", longentry) + logger.info("Short Entry : %s", shortentry) + logger.info("Long Exit : %s", longexit) + logger.info("Short Exit : %s", shortexit) + logger.info("Hold Period : %d", holdperiod) + logger.info("Scale : %r", scale) # create and run the system - if longshort: - system_ls = System(system_name, longentry, shortentry, - longexit, shortexit, holdperiod, scale) - tfs = run_system(model, system_ls, group) - else: - tfs = run_system(model, system_name, group, system_params) + system = System(system_name, longentry, shortentry, + longexit, shortexit, holdperiod, scale) + tfs = run_system(model, system, group, intraday) # generate a portfolio gen_portfolio(model, system_name, group, tfs) - else: - # run the analysis, including the model pipeline - a = Analysis(model, group) - results = run_analysis(a, lag_period, forecast_period, leaders, - predict_history) # Return the completed model return model diff --git a/alphapy/market_variables.py b/alphapy/market_variables.py index ed3b5c7..77ee8ea 100644 --- a/alphapy/market_variables.py +++ b/alphapy/market_variables.py @@ -403,10 +403,9 @@ def vexec(f, v, vfuncs=None): expr = vroot.expr expr_new = vsub(vxlag, expr) estr = "%s" % expr_new - estr = BSEP.join([vxlag, '=', estr]) logger.debug("Expression: %s", estr) # pandas eval - f.eval(estr, inplace=True) + f[vxlag] = f.eval(estr) else: logger.debug("Did not find variable: %s", root) # Must be a function call diff --git a/alphapy/model.py b/alphapy/model.py index fd07645..0bee831 100644 --- a/alphapy/model.py +++ b/alphapy/model.py @@ -39,14 +39,12 @@ from alphapy.globals import SamplingMethod from alphapy.globals import Scalers from alphapy.utilities import get_datestamp -from alphapy.utilities import np_store_data +from alphapy.utilities import most_recent_file from copy import copy from datetime import datetime -import glob import logging import numpy as np -import os import pandas as pd from sklearn.calibration import CalibratedClassifierCV from sklearn.externals import joblib @@ -476,17 +474,14 @@ def load_predictor(directory): """ - # Create search path - search_path = SSEP.join([directory, 'model', 'model_*.pkl']) - # Locate the model Pickle file try: - # find the latest file - filename = max(glob.iglob(search_path), key=os.path.getctime) - logger.info("Loading model predictor from %s", filename) + search_dir = SSEP.join([directory, 'model']) + file_name = most_recent_file(search_dir, 'model_*.pkl') + logger.info("Loading model predictor from %s", file_name) # load the model predictor - predictor = joblib.load(filename) + predictor = joblib.load(file_name) except: logging.error("Could not find model predictor in %s", search_path) @@ -555,17 +550,14 @@ def load_feature_map(model, directory): """ - # Create search path - search_path = SSEP.join([directory, 'model', 'feature_map_*.pkl']) - # Locate the feature map and load it try: - # find the latest file - filename = max(glob.iglob(search_path), key=os.path.getctime) - logger.info("Loading feature map from %s", filename) + search_dir = SSEP.join([directory, 'model']) + file_name = most_recent_file(search_dir, 'feature_map_*.pkl') + logger.info("Loading feature map from %s", file_name) # load the feature map - feature_map = joblib.load(filename) + feature_map = joblib.load(file_name) model.feature_map = feature_map except: logging.error("Could not find feature map in %s", search_path) @@ -1231,6 +1223,8 @@ def save_predictions(model, tag, partition): if found_pdate: pd_indices = pf[pf.date >= predict_date].index.tolist() pf = pf.ix[pd_indices] + else: + pd_indices = pf.index.tolist() # Save predictions for all projects @@ -1239,7 +1233,9 @@ def save_predictions(model, tag, partition): preds = model.preds[(tag, partition)] if found_pdate: preds = np.take(preds, pd_indices) - np_store_data(preds, output_dir, output_file, extension, separator) + pred_series = pd.Series(preds, index=pd_indices) + df_pred = pd.DataFrame(pred_series, columns=['prediction']) + write_frame(df_pred, output_dir, output_file, extension, separator) # Save probabilities for classification projects @@ -1250,14 +1246,16 @@ def save_predictions(model, tag, partition): probas = model.probas[(tag, partition)] if found_pdate: probas = np.take(probas, pd_indices) - np_store_data(probas, output_dir, output_file, extension, separator) + prob_series = pd.Series(probas, index=pd_indices) + df_prob = pd.DataFrame(prob_series, columns=['probability']) + write_frame(df_prob, output_dir, output_file, extension, separator) # Save ranked predictions logger.info("Saving Ranked Predictions") - pf['prediction'] = pd.Series(preds, index=pf.index) + pf['prediction'] = pred_series if model_type == ModelType.classification: - pf['probability'] = pd.Series(probas, index=pf.index) + pf['probability'] = prob_series pf.sort_values('probability', ascending=False, inplace=True) else: pf.sort_values('prediction', ascending=False, inplace=True) diff --git a/alphapy/optimize.py b/alphapy/optimize.py index 8d4d7ed..e5a601e 100644 --- a/alphapy/optimize.py +++ b/alphapy/optimize.py @@ -95,6 +95,7 @@ def rfecv_search(model, algo): # Extract model parameters. cv_folds = model.specs['cv_folds'] + n_jobs = model.specs['n_jobs'] rfe_step = model.specs['rfe_step'] scorer = model.specs['scorer'] verbosity = model.specs['verbosity'] @@ -104,7 +105,7 @@ def rfecv_search(model, algo): logger.info("Recursive Feature Elimination with CV") rfecv = RFECV(estimator, step=rfe_step, cv=cv_folds, - scoring=scorer, verbose=verbosity) + scoring=scorer, verbose=verbosity, n_jobs=n_jobs) start = time() selector = rfecv.fit(X_train, y_train) logger.info("RFECV took %.2f seconds for step %d and %d folds", diff --git a/alphapy/system.py b/alphapy/system.py index e7117fe..7c1a859 100644 --- a/alphapy/system.py +++ b/alphapy/system.py @@ -28,14 +28,18 @@ from alphapy.frame import Frame from alphapy.frame import frame_name +from alphapy.frame import read_frame from alphapy.frame import write_frame from alphapy.globals import Orders -from alphapy.globals import SSEP +from alphapy.globals import BSEP, SSEP from alphapy.market_variables import vexec from alphapy.space import Space from alphapy.portfolio import Trade +from alphapy.utilities import most_recent_file import logging +import numbers +import pandas as pd from pandas import DataFrame @@ -131,24 +135,24 @@ def __str__(self): # -# Function long_short +# Function trade_system # -def long_short(system, name, space, quantity): - r"""Run a long/short system. - - A long/short system is always in the market. At any given - time, either a long position is active, or a short position - is active. +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. - name : str - The symbol to trade. 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 @@ -163,48 +167,76 @@ def long_short(system, name, space, quantity): All of the data frames containing price data. """ - # extract the system parameters + + # 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 - # price frame + + # 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 - # initialize the trade list - tradelist = [] - # evaluate the long and short events - if longentry: - vexec(pf, longentry) - if shortentry: - vexec(pf, shortentry) - if longexit: - vexec(pf, longexit) - if shortexit: - vexec(pf, shortexit) - # generate trade file + + # 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(): - # evaluate entry and exit conditions - lerow = None - if longentry: - lerow = row[longentry] - serow = None - if shortentry: - serow = row[shortentry] - lxrow = None - if longexit: - lxrow = row[longexit] - sxrow = None - if shortexit: - sxrow = row[shortexit] # 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: @@ -244,7 +276,7 @@ def long_short(system, name, space, quantity): h = 0 p = 0 # if a holding period was given, then check for exit - if holdperiod > 0 and h >= holdperiod: + if holdperiod and h >= holdperiod: if inlong: tradelist.append((dt, [name, Orders.lh, -p, c])) inlong = False @@ -256,99 +288,17 @@ def long_short(system, name, space, quantity): # increment the hold counter if inlong or inshort: h += 1 - return tradelist - - -# -# Function open_range_breakout -# - -def open_range_breakout(name, space, quantity, t1=3, t2=12, - long_only=False): - r"""Run an Opening Range Breakout (ORB) system. - - An ORB system is an intraday strategy that waits for price to - "break out" in a certain direction after establishing an - initial High-Low range. The timing of the trade is either - time-based (e.g., 30 minutes after the Open) or price-based - (e.g., 20% of the average daily range). Either the position - is held until the end of the trading day, or the position is - closed with a stop loss (e.g., the other side of the opening - range). - - Parameters - ---------- - name : str - The symbol to trade. - space : alphapy.Space - Namespace of instrument prices. - 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. - - """ - # price frame - pf = Frame.frames[frame_name(name, space)].df - # initialize the trade list - tradelist = [] - # generate trade file - for dt, row in pf.iterrows(): - # extract data from row - bar_number = row['bar_number'] - h = row['high'] - l = row['low'] - c = row['close'] - end_of_day = row['end_of_day'] - # open range breakout - if bar_number == 0: - # new day - traded = False - inlong = False - inshort = False - hh = h - ll = l - elif bar_number < t1: - # set opening range - if h > hh: - hh = h - if l < ll: - ll = l - else: - if not traded and bar_number < t2: - # trigger trade - if h > hh: - # long breakout triggers - tradelist.append((dt, [name, Orders.le, quantity, hh])) - inlong = True - traded = True - if l < ll and not traded and not long_only: - # short breakout triggers - tradelist.append((dt, [name, Orders.se, -quantity, ll])) - inshort = True - traded = True - # test stop loss - if inlong and l < ll: - tradelist.append((dt, [name, Orders.lx, -quantity, ll])) - inlong = False - if inshort and h > hh: - tradelist.append((dt, [name, Orders.sx, quantity, hh])) - inshort = False - # exit any positions at the end of the day - if inlong and end_of_day: - # long active, so exit long - tradelist.append((dt, [name, Orders.lx, -quantity, c])) - if inshort and end_of_day: - # short active, so exit short - tradelist.append((dt, [name, Orders.sx, quantity, c])) + 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 @@ -359,7 +309,7 @@ def open_range_breakout(name, space, quantity, t1=3, t2=12, def run_system(model, system, group, - system_params=None, + intraday = False, quantity = 1): r"""Run a system for a given group, creating a trades frame. @@ -367,13 +317,12 @@ def run_system(model, ---------- model : alphapy.Model The model object with specifications. - system : alphapy.System or str - The system to run, either a long/short system or a local one - identified by function name, e.g., 'open_range_breakout'. + system : alphapy.System + The system to run. group : alphapy.Group - The group of symbols to test. - system_params : list, optional - The parameters for the given system. + The group of symbols to trade. + intraday : bool, optional + If true, this is an intraday system. quantity : float, optional The amount to trade for each symbol, e.g., number of shares @@ -384,11 +333,7 @@ def run_system(model, """ - if system.__class__ == str: - system_name = system - else: - system_name = system.name - + system_name = system.name logger.info("Generating Trades for System %s", system_name) # Unpack the model data. @@ -408,18 +353,8 @@ def run_system(model, gtlist = [] for symbol in gmembers: # generate the trades for this member - if system.__class__ == str: - try: - tlist = globals()[system_name](symbol, gspace, quantity, - *system_params) - except: - logger.info("Could not execute system for %s", symbol) - else: - # call default long/short system - tlist = long_short(system, symbol, gspace, quantity) + tlist = trade_system(model, system, gspace, intraday, symbol, quantity) if tlist: - # create the local trades frame - df = DataFrame.from_items(tlist, orient='index', columns=Trade.states) # add trades to global trade list for item in tlist: gtlist.append(item) @@ -435,8 +370,11 @@ def run_system(model, tf = DataFrame.from_items(gtlist, orient='index', columns=Trade.states) tfname = frame_name(gname, tspace) system_dir = SSEP.join([directory, 'systems']) + labels = ['date'] + if intraday: + labels.append('time') write_frame(tf, system_dir, tfname, extension, separator, - index=True, index_label='date') + index=True, index_label=labels) del tspace else: logger.info("No trades were found") diff --git a/alphapy/utilities.py b/alphapy/utilities.py index 4323bd9..f395ffe 100644 --- a/alphapy/utilities.py +++ b/alphapy/utilities.py @@ -30,10 +30,12 @@ import argparse from datetime import datetime, timedelta +import glob import inspect from itertools import groupby import logging import numpy as np +import os from os import listdir from os.path import isfile, join import re @@ -65,6 +67,34 @@ def get_datestamp(): return datestamp +# +# Function most_recent_file +# + +def most_recent_file(directory, file_spec): + r"""Find the most recent file in a directory. + + Parameters + ---------- + directory : str + Full directory specification. + file_spec : str + Wildcard search string for the file to locate. + + Returns + ------- + file_name : str + Name of the file to read, excluding the ``extension``. + + """ + # Create search path + search_path = SSEP.join([directory, file_spec]) + # find the latest file + file_name = max(glob.iglob(search_path), key=os.path.getctime) + # load the model predictor + return file_name + + # # Function np_store_data # diff --git a/docs/tutorials/closer_market.yml b/docs/tutorials/closer_market.yml index d01f3bd..76debb1 100644 --- a/docs/tutorials/closer_market.yml +++ b/docs/tutorials/closer_market.yml @@ -1,11 +1,12 @@ market: + create_model : False + data_fractal : 1d data_history : 500 forecast_period : 1 fractal : 1d lag_period : 1 leaders : [] predict_history : 50 - resample_data : False schema : quandl_wiki subject : stock target_group : faang diff --git a/docs/tutorials/rrover_market.yml b/docs/tutorials/rrover_market.yml index 3ed085e..4d9ed20 100644 --- a/docs/tutorials/rrover_market.yml +++ b/docs/tutorials/rrover_market.yml @@ -1,11 +1,12 @@ market: + create_model : True + data_fractal : 1d data_history : 500 forecast_period : 1 fractal : 1d lag_period : 1 leaders : ['gap', 'gapbadown', 'gapbaup', 'gapdown', 'gapup'] predict_history : 100 - resample_data : False schema : yahoo subject : stock target_group : test From 883d93949e9085b0133a1b2b8bd3fea25afdc554 Mon Sep 17 00:00:00 2001 From: Mark Conway Date: Thu, 25 Jan 2018 18:16:51 -0500 Subject: [PATCH 013/129] upgrade to version 2.2.2 upgrade to version 2.2.2 --- setup.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/setup.py b/setup.py index 7a5d4a2..fa27d41 100644 --- a/setup.py +++ b/setup.py @@ -10,7 +10,7 @@ MAINTAINER_EMAIL = 'scottfree.analytics@scottfreellc.com' URL = "https://github.com/ScottFreeLLC/AlphaPy" LICENSE = "Apache License, Version 2" -VERSION = "2.2.1" +VERSION = "2.2.2" classifiers = ['Development Status :: 4 - Beta', 'Programming Language :: Python', From 13c7586dd1f91eb3f980f5dcea9d36f748a05299 Mon Sep 17 00:00:00 2001 From: Mark Conway Date: Fri, 23 Mar 2018 12:39:02 -0400 Subject: [PATCH 014/129] multiprocessing start method multiprocessing start method, xgboost is hanging is gridsearchcv (not a guaranteed fix) --- alphapy/__main__.py | 2 ++ alphapy/data.py | 1 + alphapy/market_flow.py | 2 ++ alphapy/sport_flow.py | 2 ++ 4 files changed, 7 insertions(+) diff --git a/alphapy/__main__.py b/alphapy/__main__.py index 973a0b0..614ce4f 100644 --- a/alphapy/__main__.py +++ b/alphapy/__main__.py @@ -65,6 +65,7 @@ import argparse from datetime import datetime import logging +import multiprocessing as mp import numpy as np import os import pandas as pd @@ -503,4 +504,5 @@ def main(args=None): # if __name__ == "__main__": + mp.set_start_method('forkserver') main() diff --git a/alphapy/data.py b/alphapy/data.py index adfab5c..db5fe53 100644 --- a/alphapy/data.py +++ b/alphapy/data.py @@ -482,6 +482,7 @@ def get_pandas_data(schema, symbol, lookback_period): # Call the Pandas Web data reader. + df = None try: df = web.DataReader(symbol, schema, start, end) except: diff --git a/alphapy/market_flow.py b/alphapy/market_flow.py index bf1b03a..2ceba55 100644 --- a/alphapy/market_flow.py +++ b/alphapy/market_flow.py @@ -46,6 +46,7 @@ import argparse import datetime import logging +import multiprocessing as mp import os import pandas as pd import yaml @@ -427,4 +428,5 @@ def main(args=None): # if __name__ == "__main__": + mp.set_start_method('forkserver') main() diff --git a/alphapy/sport_flow.py b/alphapy/sport_flow.py index f8af0d3..9dda5ab 100644 --- a/alphapy/sport_flow.py +++ b/alphapy/sport_flow.py @@ -45,6 +45,7 @@ from itertools import groupby import logging import math +import multiprocessing as mp import numpy as np import os import pandas as pd @@ -911,4 +912,5 @@ def main(args=None): # if __name__ == "__main__": + mp.set_start_method('forkserver') main() From f5b7a3dd1abadc7efec0ba81c7b97e374e245537 Mon Sep 17 00:00:00 2001 From: Mark Conway Date: Sat, 31 Mar 2018 11:29:17 -0400 Subject: [PATCH 015/129] switch matplotlib backend fixes INVALID DISPLAY error on Ubuntu --- alphapy/plots.py | 1 + 1 file changed, 1 insertion(+) diff --git a/alphapy/plots.py b/alphapy/plots.py index 916c7d4..2b2f6b7 100644 --- a/alphapy/plots.py +++ b/alphapy/plots.py @@ -68,6 +68,7 @@ import logging import math import matplotlib.pyplot as plt +plt.switch_backend('agg') from mpl_toolkits.mplot3d import Axes3D import numpy as np import pandas as pd From 8f09ed4457b5b36c4e63886cb483ecde4c7c7459 Mon Sep 17 00:00:00 2001 From: Mark Conway Date: Sun, 1 Apr 2018 15:27:46 -0400 Subject: [PATCH 016/129] fix class_weights bug fix class_weights bug --- alphapy/model.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/alphapy/model.py b/alphapy/model.py index 0bee831..c84309c 100644 --- a/alphapy/model.py +++ b/alphapy/model.py @@ -714,7 +714,7 @@ def first_fit(model, algo, est): eval_metric = xgb_score_map[scorer] est.fit(X1, y1, eval_set=eval_set, eval_metric=eval_metric, early_stopping_rounds=esr) - elif class_weights and model_type != ModelType.classification: + elif class_weights and model_type == ModelType.classification: est.fit(X_train, y_train, sample_weight=class_weights) else: est.fit(X_train, y_train) From d7fb322aea141b4869f15786f7c0d700edaf31e9 Mon Sep 17 00:00:00 2001 From: Mark Conway Date: Mon, 21 May 2018 10:42:27 -0400 Subject: [PATCH 017/129] Keras Release - Implement KerasClassifier and KerasRegressor - Remove redundant RFE code - Add Brier Score and Cohen Kappa - Remove irrelevant metrics - Use new algos.yml file in all examples --- alphapy/__main__.py | 13 +- alphapy/estimators.py | 166 +- alphapy/examples/Kaggle/config/algos.yml | 44 +- .../Kaggle/input/gender_submission.csv | 419 ++++ alphapy/examples/NCAAB/config/algos.yml | 56 +- .../examples/Trading Model/config/algos.yml | 56 +- .../examples/Trading Model/config/market.yml | 2 +- .../examples/Trading System/config/algos.yml | 56 +- .../examples/Trading System/config/market.yml | 4 +- .../systems/faang_closer_positions_1d.csv | 499 ----- .../systems/faang_closer_returns_1d.csv | 499 ----- .../systems/faang_closer_trades_1d.csv | 1748 ----------------- .../systems/faang_closer_transactions_1d.csv | 1748 ----------------- alphapy/market_flow.py | 2 + alphapy/model.py | 224 +-- alphapy/optimize.py | 70 - alphapy/plots.py | 2 +- alphapy/sport_flow.py | 2 + 18 files changed, 727 insertions(+), 4883 deletions(-) create mode 100644 alphapy/examples/Kaggle/input/gender_submission.csv delete mode 100644 alphapy/examples/Trading System/systems/faang_closer_positions_1d.csv delete mode 100644 alphapy/examples/Trading System/systems/faang_closer_returns_1d.csv delete mode 100644 alphapy/examples/Trading System/systems/faang_closer_trades_1d.csv delete mode 100644 alphapy/examples/Trading System/systems/faang_closer_transactions_1d.csv diff --git a/alphapy/__main__.py b/alphapy/__main__.py index 614ce4f..918b20e 100644 --- a/alphapy/__main__.py +++ b/alphapy/__main__.py @@ -47,7 +47,6 @@ from alphapy.model import first_fit from alphapy.model import generate_metrics from alphapy.model import get_model_config -from alphapy.model import get_class_weights from alphapy.model import load_feature_map from alphapy.model import load_predictor from alphapy.model import make_predictions @@ -57,7 +56,6 @@ from alphapy.model import save_model from alphapy.model import save_predictions from alphapy.optimize import hyper_grid_search -from alphapy.optimize import rfe_search from alphapy.optimize import rfecv_search from alphapy.plots import generate_plots from alphapy.utilities import get_datestamp @@ -69,6 +67,7 @@ import numpy as np import os import pandas as pd +import warnings # @@ -211,8 +210,6 @@ def training_pipeline(model): model = sample_data(model) else: logger.info("Skipping Sampling") - # Get sample weights (classification only) - model = get_class_weights(model) # Perform feature selection, independent of algorithm @@ -238,7 +235,6 @@ def training_pipeline(model): # select estimator try: estimator = estimators[algo] - scoring = estimator.scoring est = estimator.estimator except KeyError: logger.info("Algorithm %s not found", algo) @@ -246,10 +242,10 @@ def training_pipeline(model): model = first_fit(model, algo, est) # recursive feature elimination if rfe: - if scoring: + has_coef = hasattr(est, "coef_") + has_fimp = hasattr(est, "feature_importances_") + if has_coef or has_fimp: model = rfecv_search(model, algo) - elif hasattr(est, "coef_"): - model = rfe_search(model, algo) else: logger.info("No RFE Available for %s", algo) # grid search @@ -504,5 +500,6 @@ def main(args=None): # if __name__ == "__main__": + warnings.filterwarnings(action='ignore', category=DeprecationWarning) mp.set_start_method('forkserver') main() diff --git a/alphapy/estimators.py b/alphapy/estimators.py index 9ebd4c9..51673c0 100644 --- a/alphapy/estimators.py +++ b/alphapy/estimators.py @@ -30,6 +30,10 @@ from alphapy.globals import Objective from alphapy.globals import SSEP +from keras.layers import * +from keras.models import Sequential +from keras.wrappers.scikit_learn import KerasClassifier +from keras.wrappers.scikit_learn import KerasRegressor import logging import numpy as np from scipy.stats import randint as sp_randint @@ -112,8 +116,6 @@ class Estimator: A scikit-learn, TensorFlow, or XGBoost function. grid : dict The dictionary of hyperparameters for grid search. - scoring : bool, optional - Use a scoring function to evaluate the best model. """ @@ -123,8 +125,7 @@ def __new__(cls, algorithm, model_type, estimator, - grid, - scoring=False): + grid): return super(Estimator, cls).__new__(cls) # __init__ @@ -133,13 +134,11 @@ def __init__(self, algorithm, model_type, estimator, - grid, - scoring=False): + grid): self.algorithm = algorithm.upper() self.model_type = model_type self.estimator = estimator self.grid = grid - self.scoring = scoring # __str__ @@ -147,60 +146,15 @@ def __str__(self): return self.name -# -# Classes -# - -class AdaBoostClassifierCoef(AdaBoostClassifier): - """An AdaBoost classifier where the coefficients are set to - the feature importances for Recursive Feature Elimination - to work. - - """ - def fit(self, *args, **kwargs): - super(AdaBoostClassifierCoef, self).fit(*args, **kwargs) - self.coef_ = self.feature_importances_ - - -class ExtraTreesClassifierCoef(ExtraTreesClassifier): - """An Extra Trees classifier where the coefficients are set to - the feature importances for Recursive Feature Elimination - to work. - - """ - def fit(self, *args, **kwargs): - super(ExtraTreesClassifierCoef, self).fit(*args, **kwargs) - self.coef_ = self.feature_importances_ - - -class RandomForestClassifierCoef(RandomForestClassifier): - """A Random Forest classifier where the coefficients are set to - the feature importances for Recursive Feature Elimination - to work. - - """ - def fit(self, *args, **kwargs): - super(RandomForestClassifierCoef, self).fit(*args, **kwargs) - self.coef_ = self.feature_importances_ - -class GradientBoostingClassifierCoef(GradientBoostingClassifier): - """A Gradient Boostin classifier where the coefficients are set to - the feature importances for Recursive Feature Elimination - to work. - - """ - def fit(self, *args, **kwargs): - super(GradientBoostingClassifierCoef, self).fit(*args, **kwargs) - self.coef_ = self.feature_importances_ - - # # Define estimator map # -estimator_map = {'AB' : AdaBoostClassifierCoef, - 'GB' : GradientBoostingClassifierCoef, +estimator_map = {'AB' : AdaBoostClassifier, + 'GB' : GradientBoostingClassifier, 'GBR' : GradientBoostingRegressor, + 'KERASC' : KerasClassifier, + 'KERASR' : KerasRegressor, 'KNN' : KNeighborsClassifier, 'KNR' : KNeighborsRegressor, 'LOGR' : LogisticRegression, @@ -209,13 +163,13 @@ def fit(self, *args, **kwargs): 'LSVM' : SVC, 'NB' : MultinomialNB, 'RBF' : SVC, - 'RF' : RandomForestClassifierCoef, + 'RF' : RandomForestClassifier, 'RFR' : RandomForestRegressor, 'SVM' : SVC, 'XGB' : xgb.XGBClassifier, 'XGBM' : xgb.XGBClassifier, 'XGBR' : xgb.XGBRegressor, - 'XT' : ExtraTreesClassifierCoef, + 'XT' : ExtraTreesClassifier, 'XTR' : ExtraTreesRegressor } @@ -250,11 +204,16 @@ def get_algos_config(cfg_dir): # Ensure each algorithm has required keys - required_keys = ['model_type', 'params', 'grid', 'scoring'] + minimum_keys = ['model_type', 'params', 'grid'] + required_keys_keras = minimum_keys + ['layers', 'compiler'] for algo in specs: + if 'KERAS' in algo: + required_keys = required_keys_keras + else: + required_keys = minimum_keys algo_keys = list(specs[algo].keys()) if set(algo_keys) != set(required_keys): - logger.warning("Algorithm %s is missing the required keys %s", + logger.warning("Algorithm %s has the wrong keys %s", algo, required_keys) logger.warning("Keys found instead: %s", algo_keys) else: @@ -271,24 +230,57 @@ def get_algos_config(cfg_dir): # -# Function get_estimators +# Function create_keras_model # -# AdaBoost (feature_importances_) -# Gradient Boosting (feature_importances_) -# K-Nearest Neighbors (NA) -# Linear Regression (coef_) -# Linear Support Vector Machine (coef_) -# Logistic Regression (coef_) -# Naive Bayes (coef_) -# Radial Basis Function (NA) -# Random Forest (feature_importances_) -# Support Vector Machine (NA) -# XGBoost Binary (NA) -# XGBoost Multi (NA) -# Extra Trees (feature_importances_) -# Random Forest (feature_importances_) -# Randomized Lasso +def create_keras_model(nlayers, + layer1=None, + layer2=None, + layer3=None, + layer4=None, + layer5=None, + layer6=None, + layer7=None, + layer8=None, + layer9=None, + layer10=None, + optimizer=None, + loss=None, + metrics=None): + r"""Create a Keras Sequential model. + + Parameters + ---------- + nlayers : int + Number of layers of the Sequential model. + layer1...layer10 : str + Ordered layers of the Sequential model. + optimizer : str + Compiler optimizer for the Sequential model. + loss : str + Compiler loss function for the Sequential model. + metrics : str + Compiler evaluation metric for the Sequential model. + + Returns + ------- + model : keras.models.Sequential + Compiled Keras Sequential Model. + + """ + + model = Sequential() + for i in range(nlayers): + lvar = 'layer' + str(i+1) + layer = eval(lvar) + model.add(eval(layer)) + model.compile(optimizer=optimizer, loss=loss, metrics=[metrics]) + return model + + +# +# Function get_estimators +# def get_estimators(model): r"""Define all the AlphaPy estimators based on the contents @@ -314,10 +306,14 @@ def get_estimators(model): seed = model.specs['seed'] verbosity = model.specs['verbosity'] + # Reference training data for Keras input_dim + X_train = model.X_train + # Initialize estimator dictionary estimators = {} # Global parameter substitution fields + ps_fields = {'n_estimators' : 'n_estimators', 'n_jobs' : 'n_jobs', 'nthread' : 'n_jobs', @@ -339,10 +335,24 @@ def get_estimators(model): if param in ps_fields and isinstance(param, str): algo_specs[algo]['params'][param] = eval(ps_fields[param]) func = estimator_map[algo] + if 'KERAS' in algo: + params['build_fn'] = create_keras_model + layers = algo_specs[algo]['layers'] + params['nlayers'] = len(layers) + input_dim_string = ', input_dim={})'.format(X_train.shape[1]) + layers[0] = layers[0].replace(')', input_dim_string) + for i, layer in enumerate(layers): + params['layer'+str(i+1)] = layer + compiler = algo_specs[algo]['compiler'] + params['optimizer'] = compiler['optimizer'] + params['loss'] = compiler['loss'] + try: + params['metrics'] = compiler['metrics'] + except: + pass est = func(**params) grid = algo_specs[algo]['grid'] - scoring = algo_specs[algo]['scoring'] - estimators[algo] = Estimator(algo, model_type, est, grid, scoring) + estimators[algo] = Estimator(algo, model_type, est, grid) # return the entire classifier list return estimators diff --git a/alphapy/examples/Kaggle/config/algos.yml b/alphapy/examples/Kaggle/config/algos.yml index ab4678a..17ebce6 100644 --- a/alphapy/examples/Kaggle/config/algos.yml +++ b/alphapy/examples/Kaggle/config/algos.yml @@ -10,7 +10,6 @@ AB: grid : {"n_estimators" : [10, 50, 100, 150, 200], "learning_rate" : [0.2, 0.5, 0.7, 1.0, 1.5, 2.0], "algorithm" : ['SAMME', 'SAMME.R']} - scoring : True GB: # Gradient Boosting @@ -25,7 +24,6 @@ GB: "max_depth" : [3, 5, 10], "min_samples_split" : [2, 3], "min_samples_leaf" : [1, 2]} - scoring : True GBR: # Gradient Boosting Regression @@ -34,7 +32,31 @@ GBR: "random_state" : seed, "verbose" : verbosity} grid : {} - scoring : False + +KERASC: + # Keras Classification + model_type : classification + layers : ["Dense(12, activation='relu')", + "Dense(1, activation='sigmoid')"] + compiler : {"optimizer" : 'rmsprop', + "loss" : 'binary_crossentropy', + "metrics" : 'accuracy'} + params : {"epochs" : 50, + "batch_size" : 10, + "verbose" : 1} + grid : {} + +KERASR: + # Keras Regression + model_type : regression + layers : ["Dense(10, activation='relu')", + "Dense(1)"] + compiler : {"optimizer" : 'rmsprop', + "loss" : 'mse'} + params : {"epochs" : 50, + "batch_size" : 10, + "verbose" : 1} + grid : {} KNN: # K-Nearest Neighbors @@ -44,14 +66,12 @@ KNN: "weights" : ['uniform', 'distance'], "algorithm" : ['ball_tree', 'kd_tree', 'brute', 'auto'], "leaf_size" : [10, 20, 30, 40, 50]} - scoring : False KNR: # K-Nearest Neighbor Regression model_type : regression params : {"n_jobs" : n_jobs} grid : {} - scoring : False LOGR: # Logistic Regression @@ -63,7 +83,6 @@ LOGR: "C" : [0.00001, 0.0001, 0.001, 0.01, 0.1, 1, 10, 100, 1000, 1e4, 1e5, 1e6, 1e7], "fit_intercept" : [True, False], "solver" : ['newton-cg', 'lbfgs', 'liblinear', 'sag']} - scoring : True LR: # Linear Regression @@ -72,7 +91,6 @@ LR: grid : {"fit_intercept" : [True, False], "normalize" : [True, False], "copy_X" : [True, False]} - scoring : False LSVC: # Linear Support Vector Classification @@ -88,7 +106,6 @@ LSVC: "dual" : [True, False], "tol" : [0.0005, 0.001, 0.005], "max_iter" : [500, 1000, 2000]} - scoring : False LSVM: # Linear Support Vector Machine @@ -102,7 +119,6 @@ LSVM: "shrinking" : [True, False], "tol" : [0.0005, 0.001, 0.005], "decision_function_shape" : ['ovo', 'ovr']} - scoring : False NB: # Naive Bayes @@ -110,7 +126,6 @@ NB: params : {} grid : {"alpha" : [0.01, 0.1, 0.2, 0.3, 0.4, 0.5, 1.0, 2.0, 5.0, 10.0], "fit_prior" : [True, False]} - scoring : True RBF: # Radial Basis Function @@ -124,7 +139,6 @@ RBF: "shrinking" : [True, False], "tol" : [0.0005, 0.001, 0.005], "decision_function_shape" : ['ovo', 'ovr']} - scoring : False RF: # Random Forest @@ -144,7 +158,6 @@ RF: "min_samples_leaf" : [1, 2, 3], "bootstrap" : [True, False], "criterion" : ['gini', 'entropy']} - scoring : True RFR: # Random Forest Regression @@ -154,7 +167,6 @@ RFR: "n_jobs" : n_jobs, "verbose" : verbosity} grid : {} - scoring : False SVM: # Support Vector Machine @@ -167,7 +179,6 @@ SVM: "shrinking" : [True, False], "tol" : [0.0005, 0.001, 0.005], "decision_function_shape" : ['ovo', 'ovr']} - scoring : False XGB: # XGBoost Binary @@ -188,7 +199,6 @@ XGB: "min_child_weight" : [1.0, 1.1], "subsample" : [0.5, 0.6, 0.7, 0.8, 0.9, 1.0], "colsample_bytree" : [0.5, 0.6, 0.7, 0.8, 0.9, 1.0]} - scoring : False XGBM: # XGBoost Multiclass @@ -204,7 +214,6 @@ XGBM: "nthread" : n_jobs, "silent" : True} grid : {} - scoring : False XGBR: # XGBoost Regression @@ -221,7 +230,6 @@ XGBR: "nthread" : n_jobs, "silent" : True} grid : {} - scoring : False XT: # Extra Trees @@ -237,7 +245,6 @@ XT: "min_samples_leaf" : [1, 2], "bootstrap" : [True, False], "warm_start" : [True, False]} - scoring : True XTR: # Extra Trees Regression @@ -247,4 +254,3 @@ XTR: "n_jobs" : n_jobs, "verbose" : verbosity} grid : {} - scoring : False diff --git a/alphapy/examples/Kaggle/input/gender_submission.csv b/alphapy/examples/Kaggle/input/gender_submission.csv new file mode 100644 index 0000000..7594506 --- /dev/null +++ b/alphapy/examples/Kaggle/input/gender_submission.csv @@ -0,0 +1,419 @@ +PassengerId,Survived +892,0 +893,1 +894,0 +895,0 +896,1 +897,0 +898,1 +899,0 +900,1 +901,0 +902,0 +903,0 +904,1 +905,0 +906,1 +907,1 +908,0 +909,0 +910,1 +911,1 +912,0 +913,0 +914,1 +915,0 +916,1 +917,0 +918,1 +919,0 +920,0 +921,0 +922,0 +923,0 +924,1 +925,1 +926,0 +927,0 +928,1 +929,1 +930,0 +931,0 +932,0 +933,0 +934,0 +935,1 +936,1 +937,0 +938,0 +939,0 +940,1 +941,1 +942,0 +943,0 +944,1 +945,1 +946,0 +947,0 +948,0 +949,0 +950,0 +951,1 +952,0 +953,0 +954,0 +955,1 +956,0 +957,1 +958,1 +959,0 +960,0 +961,1 +962,1 +963,0 +964,1 +965,0 +966,1 +967,0 +968,0 +969,1 +970,0 +971,1 +972,0 +973,0 +974,0 +975,0 +976,0 +977,0 +978,1 +979,1 +980,1 +981,0 +982,1 +983,0 +984,1 +985,0 +986,0 +987,0 +988,1 +989,0 +990,1 +991,0 +992,1 +993,0 +994,0 +995,0 +996,1 +997,0 +998,0 +999,0 +1000,0 +1001,0 +1002,0 +1003,1 +1004,1 +1005,1 +1006,1 +1007,0 +1008,0 +1009,1 +1010,0 +1011,1 +1012,1 +1013,0 +1014,1 +1015,0 +1016,0 +1017,1 +1018,0 +1019,1 +1020,0 +1021,0 +1022,0 +1023,0 +1024,1 +1025,0 +1026,0 +1027,0 +1028,0 +1029,0 +1030,1 +1031,0 +1032,1 +1033,1 +1034,0 +1035,0 +1036,0 +1037,0 +1038,0 +1039,0 +1040,0 +1041,0 +1042,1 +1043,0 +1044,0 +1045,1 +1046,0 +1047,0 +1048,1 +1049,1 +1050,0 +1051,1 +1052,1 +1053,0 +1054,1 +1055,0 +1056,0 +1057,1 +1058,0 +1059,0 +1060,1 +1061,1 +1062,0 +1063,0 +1064,0 +1065,0 +1066,0 +1067,1 +1068,1 +1069,0 +1070,1 +1071,1 +1072,0 +1073,0 +1074,1 +1075,0 +1076,1 +1077,0 +1078,1 +1079,0 +1080,1 +1081,0 +1082,0 +1083,0 +1084,0 +1085,0 +1086,0 +1087,0 +1088,0 +1089,1 +1090,0 +1091,1 +1092,1 +1093,0 +1094,0 +1095,1 +1096,0 +1097,0 +1098,1 +1099,0 +1100,1 +1101,0 +1102,0 +1103,0 +1104,0 +1105,1 +1106,1 +1107,0 +1108,1 +1109,0 +1110,1 +1111,0 +1112,1 +1113,0 +1114,1 +1115,0 +1116,1 +1117,1 +1118,0 +1119,1 +1120,0 +1121,0 +1122,0 +1123,1 +1124,0 +1125,0 +1126,0 +1127,0 +1128,0 +1129,0 +1130,1 +1131,1 +1132,1 +1133,1 +1134,0 +1135,0 +1136,0 +1137,0 +1138,1 +1139,0 +1140,1 +1141,1 +1142,1 +1143,0 +1144,0 +1145,0 +1146,0 +1147,0 +1148,0 +1149,0 +1150,1 +1151,0 +1152,0 +1153,0 +1154,1 +1155,1 +1156,0 +1157,0 +1158,0 +1159,0 +1160,1 +1161,0 +1162,0 +1163,0 +1164,1 +1165,1 +1166,0 +1167,1 +1168,0 +1169,0 +1170,0 +1171,0 +1172,1 +1173,0 +1174,1 +1175,1 +1176,1 +1177,0 +1178,0 +1179,0 +1180,0 +1181,0 +1182,0 +1183,1 +1184,0 +1185,0 +1186,0 +1187,0 +1188,1 +1189,0 +1190,0 +1191,0 +1192,0 +1193,0 +1194,0 +1195,0 +1196,1 +1197,1 +1198,0 +1199,0 +1200,0 +1201,1 +1202,0 +1203,0 +1204,0 +1205,1 +1206,1 +1207,1 +1208,0 +1209,0 +1210,0 +1211,0 +1212,0 +1213,0 +1214,0 +1215,0 +1216,1 +1217,0 +1218,1 +1219,0 +1220,0 +1221,0 +1222,1 +1223,0 +1224,0 +1225,1 +1226,0 +1227,0 +1228,0 +1229,0 +1230,0 +1231,0 +1232,0 +1233,0 +1234,0 +1235,1 +1236,0 +1237,1 +1238,0 +1239,1 +1240,0 +1241,1 +1242,1 +1243,0 +1244,0 +1245,0 +1246,1 +1247,0 +1248,1 +1249,0 +1250,0 +1251,1 +1252,0 +1253,1 +1254,1 +1255,0 +1256,1 +1257,1 +1258,0 +1259,1 +1260,1 +1261,0 +1262,0 +1263,1 +1264,0 +1265,0 +1266,1 +1267,1 +1268,1 +1269,0 +1270,0 +1271,0 +1272,0 +1273,0 +1274,1 +1275,1 +1276,0 +1277,1 +1278,0 +1279,0 +1280,0 +1281,0 +1282,0 +1283,1 +1284,0 +1285,0 +1286,0 +1287,1 +1288,0 +1289,1 +1290,0 +1291,0 +1292,1 +1293,0 +1294,1 +1295,0 +1296,0 +1297,0 +1298,0 +1299,0 +1300,1 +1301,1 +1302,1 +1303,1 +1304,1 +1305,0 +1306,1 +1307,0 +1308,0 +1309,0 diff --git a/alphapy/examples/NCAAB/config/algos.yml b/alphapy/examples/NCAAB/config/algos.yml index 73155fe..17ebce6 100644 --- a/alphapy/examples/NCAAB/config/algos.yml +++ b/alphapy/examples/NCAAB/config/algos.yml @@ -10,7 +10,6 @@ AB: grid : {"n_estimators" : [10, 50, 100, 150, 200], "learning_rate" : [0.2, 0.5, 0.7, 1.0, 1.5, 2.0], "algorithm" : ['SAMME', 'SAMME.R']} - scoring : True GB: # Gradient Boosting @@ -25,7 +24,6 @@ GB: "max_depth" : [3, 5, 10], "min_samples_split" : [2, 3], "min_samples_leaf" : [1, 2]} - scoring : True GBR: # Gradient Boosting Regression @@ -34,7 +32,31 @@ GBR: "random_state" : seed, "verbose" : verbosity} grid : {} - scoring : False + +KERASC: + # Keras Classification + model_type : classification + layers : ["Dense(12, activation='relu')", + "Dense(1, activation='sigmoid')"] + compiler : {"optimizer" : 'rmsprop', + "loss" : 'binary_crossentropy', + "metrics" : 'accuracy'} + params : {"epochs" : 50, + "batch_size" : 10, + "verbose" : 1} + grid : {} + +KERASR: + # Keras Regression + model_type : regression + layers : ["Dense(10, activation='relu')", + "Dense(1)"] + compiler : {"optimizer" : 'rmsprop', + "loss" : 'mse'} + params : {"epochs" : 50, + "batch_size" : 10, + "verbose" : 1} + grid : {} KNN: # K-Nearest Neighbors @@ -44,14 +66,12 @@ KNN: "weights" : ['uniform', 'distance'], "algorithm" : ['ball_tree', 'kd_tree', 'brute', 'auto'], "leaf_size" : [10, 20, 30, 40, 50]} - scoring : False KNR: # K-Nearest Neighbor Regression model_type : regression params : {"n_jobs" : n_jobs} grid : {} - scoring : False LOGR: # Logistic Regression @@ -63,7 +83,6 @@ LOGR: "C" : [0.00001, 0.0001, 0.001, 0.01, 0.1, 1, 10, 100, 1000, 1e4, 1e5, 1e6, 1e7], "fit_intercept" : [True, False], "solver" : ['newton-cg', 'lbfgs', 'liblinear', 'sag']} - scoring : True LR: # Linear Regression @@ -72,7 +91,6 @@ LR: grid : {"fit_intercept" : [True, False], "normalize" : [True, False], "copy_X" : [True, False]} - scoring : False LSVC: # Linear Support Vector Classification @@ -88,7 +106,6 @@ LSVC: "dual" : [True, False], "tol" : [0.0005, 0.001, 0.005], "max_iter" : [500, 1000, 2000]} - scoring : False LSVM: # Linear Support Vector Machine @@ -102,7 +119,6 @@ LSVM: "shrinking" : [True, False], "tol" : [0.0005, 0.001, 0.005], "decision_function_shape" : ['ovo', 'ovr']} - scoring : False NB: # Naive Bayes @@ -110,7 +126,6 @@ NB: params : {} grid : {"alpha" : [0.01, 0.1, 0.2, 0.3, 0.4, 0.5, 1.0, 2.0, 5.0, 10.0], "fit_prior" : [True, False]} - scoring : True RBF: # Radial Basis Function @@ -124,7 +139,6 @@ RBF: "shrinking" : [True, False], "tol" : [0.0005, 0.001, 0.005], "decision_function_shape" : ['ovo', 'ovr']} - scoring : False RF: # Random Forest @@ -144,7 +158,6 @@ RF: "min_samples_leaf" : [1, 2, 3], "bootstrap" : [True, False], "criterion" : ['gini', 'entropy']} - scoring : True RFR: # Random Forest Regression @@ -154,7 +167,6 @@ RFR: "n_jobs" : n_jobs, "verbose" : verbosity} grid : {} - scoring : False SVM: # Support Vector Machine @@ -167,19 +179,18 @@ SVM: "shrinking" : [True, False], "tol" : [0.0005, 0.001, 0.005], "decision_function_shape" : ['ovo', 'ovr']} - scoring : False XGB: # XGBoost Binary model_type : classification params : {"objective" : 'binary:logistic', - "n_estimators" : n_estimators, + "n_estimators" : 300, "seed" : seed, - "max_depth" : 6, - "learning_rate" : 0.1, - "min_child_weight" : 1.1, - "subsample" : 0.9, - "colsample_bytree" : 0.9, + "max_depth" : 3, + "learning_rate" : 0.05, + "min_child_weight" : 1.0, + "subsample" : 1.0, + "colsample_bytree" : 1.0, "nthread" : n_jobs, "silent" : True} grid : {"n_estimators" : [21, 51, 101, 201, 501], @@ -188,7 +199,6 @@ XGB: "min_child_weight" : [1.0, 1.1], "subsample" : [0.5, 0.6, 0.7, 0.8, 0.9, 1.0], "colsample_bytree" : [0.5, 0.6, 0.7, 0.8, 0.9, 1.0]} - scoring : False XGBM: # XGBoost Multiclass @@ -204,7 +214,6 @@ XGBM: "nthread" : n_jobs, "silent" : True} grid : {} - scoring : False XGBR: # XGBoost Regression @@ -221,7 +230,6 @@ XGBR: "nthread" : n_jobs, "silent" : True} grid : {} - scoring : False XT: # Extra Trees @@ -237,7 +245,6 @@ XT: "min_samples_leaf" : [1, 2], "bootstrap" : [True, False], "warm_start" : [True, False]} - scoring : True XTR: # Extra Trees Regression @@ -247,4 +254,3 @@ XTR: "n_jobs" : n_jobs, "verbose" : verbosity} grid : {} - scoring : False diff --git a/alphapy/examples/Trading Model/config/algos.yml b/alphapy/examples/Trading Model/config/algos.yml index 73155fe..17ebce6 100644 --- a/alphapy/examples/Trading Model/config/algos.yml +++ b/alphapy/examples/Trading Model/config/algos.yml @@ -10,7 +10,6 @@ AB: grid : {"n_estimators" : [10, 50, 100, 150, 200], "learning_rate" : [0.2, 0.5, 0.7, 1.0, 1.5, 2.0], "algorithm" : ['SAMME', 'SAMME.R']} - scoring : True GB: # Gradient Boosting @@ -25,7 +24,6 @@ GB: "max_depth" : [3, 5, 10], "min_samples_split" : [2, 3], "min_samples_leaf" : [1, 2]} - scoring : True GBR: # Gradient Boosting Regression @@ -34,7 +32,31 @@ GBR: "random_state" : seed, "verbose" : verbosity} grid : {} - scoring : False + +KERASC: + # Keras Classification + model_type : classification + layers : ["Dense(12, activation='relu')", + "Dense(1, activation='sigmoid')"] + compiler : {"optimizer" : 'rmsprop', + "loss" : 'binary_crossentropy', + "metrics" : 'accuracy'} + params : {"epochs" : 50, + "batch_size" : 10, + "verbose" : 1} + grid : {} + +KERASR: + # Keras Regression + model_type : regression + layers : ["Dense(10, activation='relu')", + "Dense(1)"] + compiler : {"optimizer" : 'rmsprop', + "loss" : 'mse'} + params : {"epochs" : 50, + "batch_size" : 10, + "verbose" : 1} + grid : {} KNN: # K-Nearest Neighbors @@ -44,14 +66,12 @@ KNN: "weights" : ['uniform', 'distance'], "algorithm" : ['ball_tree', 'kd_tree', 'brute', 'auto'], "leaf_size" : [10, 20, 30, 40, 50]} - scoring : False KNR: # K-Nearest Neighbor Regression model_type : regression params : {"n_jobs" : n_jobs} grid : {} - scoring : False LOGR: # Logistic Regression @@ -63,7 +83,6 @@ LOGR: "C" : [0.00001, 0.0001, 0.001, 0.01, 0.1, 1, 10, 100, 1000, 1e4, 1e5, 1e6, 1e7], "fit_intercept" : [True, False], "solver" : ['newton-cg', 'lbfgs', 'liblinear', 'sag']} - scoring : True LR: # Linear Regression @@ -72,7 +91,6 @@ LR: grid : {"fit_intercept" : [True, False], "normalize" : [True, False], "copy_X" : [True, False]} - scoring : False LSVC: # Linear Support Vector Classification @@ -88,7 +106,6 @@ LSVC: "dual" : [True, False], "tol" : [0.0005, 0.001, 0.005], "max_iter" : [500, 1000, 2000]} - scoring : False LSVM: # Linear Support Vector Machine @@ -102,7 +119,6 @@ LSVM: "shrinking" : [True, False], "tol" : [0.0005, 0.001, 0.005], "decision_function_shape" : ['ovo', 'ovr']} - scoring : False NB: # Naive Bayes @@ -110,7 +126,6 @@ NB: params : {} grid : {"alpha" : [0.01, 0.1, 0.2, 0.3, 0.4, 0.5, 1.0, 2.0, 5.0, 10.0], "fit_prior" : [True, False]} - scoring : True RBF: # Radial Basis Function @@ -124,7 +139,6 @@ RBF: "shrinking" : [True, False], "tol" : [0.0005, 0.001, 0.005], "decision_function_shape" : ['ovo', 'ovr']} - scoring : False RF: # Random Forest @@ -144,7 +158,6 @@ RF: "min_samples_leaf" : [1, 2, 3], "bootstrap" : [True, False], "criterion" : ['gini', 'entropy']} - scoring : True RFR: # Random Forest Regression @@ -154,7 +167,6 @@ RFR: "n_jobs" : n_jobs, "verbose" : verbosity} grid : {} - scoring : False SVM: # Support Vector Machine @@ -167,19 +179,18 @@ SVM: "shrinking" : [True, False], "tol" : [0.0005, 0.001, 0.005], "decision_function_shape" : ['ovo', 'ovr']} - scoring : False XGB: # XGBoost Binary model_type : classification params : {"objective" : 'binary:logistic', - "n_estimators" : n_estimators, + "n_estimators" : 300, "seed" : seed, - "max_depth" : 6, - "learning_rate" : 0.1, - "min_child_weight" : 1.1, - "subsample" : 0.9, - "colsample_bytree" : 0.9, + "max_depth" : 3, + "learning_rate" : 0.05, + "min_child_weight" : 1.0, + "subsample" : 1.0, + "colsample_bytree" : 1.0, "nthread" : n_jobs, "silent" : True} grid : {"n_estimators" : [21, 51, 101, 201, 501], @@ -188,7 +199,6 @@ XGB: "min_child_weight" : [1.0, 1.1], "subsample" : [0.5, 0.6, 0.7, 0.8, 0.9, 1.0], "colsample_bytree" : [0.5, 0.6, 0.7, 0.8, 0.9, 1.0]} - scoring : False XGBM: # XGBoost Multiclass @@ -204,7 +214,6 @@ XGBM: "nthread" : n_jobs, "silent" : True} grid : {} - scoring : False XGBR: # XGBoost Regression @@ -221,7 +230,6 @@ XGBR: "nthread" : n_jobs, "silent" : True} grid : {} - scoring : False XT: # Extra Trees @@ -237,7 +245,6 @@ XT: "min_samples_leaf" : [1, 2], "bootstrap" : [True, False], "warm_start" : [True, False]} - scoring : True XTR: # Extra Trees Regression @@ -247,4 +254,3 @@ XTR: "n_jobs" : n_jobs, "verbose" : verbosity} grid : {} - scoring : False diff --git a/alphapy/examples/Trading Model/config/market.yml b/alphapy/examples/Trading Model/config/market.yml index 4d9ed20..39d392d 100644 --- a/alphapy/examples/Trading Model/config/market.yml +++ b/alphapy/examples/Trading Model/config/market.yml @@ -7,7 +7,7 @@ market: lag_period : 1 leaders : ['gap', 'gapbadown', 'gapbaup', 'gapdown', 'gapup'] predict_history : 100 - schema : yahoo + schema : quandl_wiki subject : stock target_group : test diff --git a/alphapy/examples/Trading System/config/algos.yml b/alphapy/examples/Trading System/config/algos.yml index 73155fe..17ebce6 100644 --- a/alphapy/examples/Trading System/config/algos.yml +++ b/alphapy/examples/Trading System/config/algos.yml @@ -10,7 +10,6 @@ AB: grid : {"n_estimators" : [10, 50, 100, 150, 200], "learning_rate" : [0.2, 0.5, 0.7, 1.0, 1.5, 2.0], "algorithm" : ['SAMME', 'SAMME.R']} - scoring : True GB: # Gradient Boosting @@ -25,7 +24,6 @@ GB: "max_depth" : [3, 5, 10], "min_samples_split" : [2, 3], "min_samples_leaf" : [1, 2]} - scoring : True GBR: # Gradient Boosting Regression @@ -34,7 +32,31 @@ GBR: "random_state" : seed, "verbose" : verbosity} grid : {} - scoring : False + +KERASC: + # Keras Classification + model_type : classification + layers : ["Dense(12, activation='relu')", + "Dense(1, activation='sigmoid')"] + compiler : {"optimizer" : 'rmsprop', + "loss" : 'binary_crossentropy', + "metrics" : 'accuracy'} + params : {"epochs" : 50, + "batch_size" : 10, + "verbose" : 1} + grid : {} + +KERASR: + # Keras Regression + model_type : regression + layers : ["Dense(10, activation='relu')", + "Dense(1)"] + compiler : {"optimizer" : 'rmsprop', + "loss" : 'mse'} + params : {"epochs" : 50, + "batch_size" : 10, + "verbose" : 1} + grid : {} KNN: # K-Nearest Neighbors @@ -44,14 +66,12 @@ KNN: "weights" : ['uniform', 'distance'], "algorithm" : ['ball_tree', 'kd_tree', 'brute', 'auto'], "leaf_size" : [10, 20, 30, 40, 50]} - scoring : False KNR: # K-Nearest Neighbor Regression model_type : regression params : {"n_jobs" : n_jobs} grid : {} - scoring : False LOGR: # Logistic Regression @@ -63,7 +83,6 @@ LOGR: "C" : [0.00001, 0.0001, 0.001, 0.01, 0.1, 1, 10, 100, 1000, 1e4, 1e5, 1e6, 1e7], "fit_intercept" : [True, False], "solver" : ['newton-cg', 'lbfgs', 'liblinear', 'sag']} - scoring : True LR: # Linear Regression @@ -72,7 +91,6 @@ LR: grid : {"fit_intercept" : [True, False], "normalize" : [True, False], "copy_X" : [True, False]} - scoring : False LSVC: # Linear Support Vector Classification @@ -88,7 +106,6 @@ LSVC: "dual" : [True, False], "tol" : [0.0005, 0.001, 0.005], "max_iter" : [500, 1000, 2000]} - scoring : False LSVM: # Linear Support Vector Machine @@ -102,7 +119,6 @@ LSVM: "shrinking" : [True, False], "tol" : [0.0005, 0.001, 0.005], "decision_function_shape" : ['ovo', 'ovr']} - scoring : False NB: # Naive Bayes @@ -110,7 +126,6 @@ NB: params : {} grid : {"alpha" : [0.01, 0.1, 0.2, 0.3, 0.4, 0.5, 1.0, 2.0, 5.0, 10.0], "fit_prior" : [True, False]} - scoring : True RBF: # Radial Basis Function @@ -124,7 +139,6 @@ RBF: "shrinking" : [True, False], "tol" : [0.0005, 0.001, 0.005], "decision_function_shape" : ['ovo', 'ovr']} - scoring : False RF: # Random Forest @@ -144,7 +158,6 @@ RF: "min_samples_leaf" : [1, 2, 3], "bootstrap" : [True, False], "criterion" : ['gini', 'entropy']} - scoring : True RFR: # Random Forest Regression @@ -154,7 +167,6 @@ RFR: "n_jobs" : n_jobs, "verbose" : verbosity} grid : {} - scoring : False SVM: # Support Vector Machine @@ -167,19 +179,18 @@ SVM: "shrinking" : [True, False], "tol" : [0.0005, 0.001, 0.005], "decision_function_shape" : ['ovo', 'ovr']} - scoring : False XGB: # XGBoost Binary model_type : classification params : {"objective" : 'binary:logistic', - "n_estimators" : n_estimators, + "n_estimators" : 300, "seed" : seed, - "max_depth" : 6, - "learning_rate" : 0.1, - "min_child_weight" : 1.1, - "subsample" : 0.9, - "colsample_bytree" : 0.9, + "max_depth" : 3, + "learning_rate" : 0.05, + "min_child_weight" : 1.0, + "subsample" : 1.0, + "colsample_bytree" : 1.0, "nthread" : n_jobs, "silent" : True} grid : {"n_estimators" : [21, 51, 101, 201, 501], @@ -188,7 +199,6 @@ XGB: "min_child_weight" : [1.0, 1.1], "subsample" : [0.5, 0.6, 0.7, 0.8, 0.9, 1.0], "colsample_bytree" : [0.5, 0.6, 0.7, 0.8, 0.9, 1.0]} - scoring : False XGBM: # XGBoost Multiclass @@ -204,7 +214,6 @@ XGBM: "nthread" : n_jobs, "silent" : True} grid : {} - scoring : False XGBR: # XGBoost Regression @@ -221,7 +230,6 @@ XGBR: "nthread" : n_jobs, "silent" : True} grid : {} - scoring : False XT: # Extra Trees @@ -237,7 +245,6 @@ XT: "min_samples_leaf" : [1, 2], "bootstrap" : [True, False], "warm_start" : [True, False]} - scoring : True XTR: # Extra Trees Regression @@ -247,4 +254,3 @@ XTR: "n_jobs" : n_jobs, "verbose" : verbosity} grid : {} - scoring : False diff --git a/alphapy/examples/Trading System/config/market.yml b/alphapy/examples/Trading System/config/market.yml index 76debb1..002087d 100644 --- a/alphapy/examples/Trading System/config/market.yml +++ b/alphapy/examples/Trading System/config/market.yml @@ -12,7 +12,7 @@ market: target_group : faang system: - name : 'closer' + name : closer holdperiod : 0 longentry : hc longexit : @@ -23,8 +23,6 @@ system: groups: faang : ['fb', 'aapl', 'amzn', 'nflx', 'googl'] -features : ['hc', 'lc'] - aliases: hc : 'higher_close' lc : 'lower_close' diff --git a/alphapy/examples/Trading System/systems/faang_closer_positions_1d.csv b/alphapy/examples/Trading System/systems/faang_closer_positions_1d.csv deleted file mode 100644 index 926344a..0000000 --- a/alphapy/examples/Trading System/systems/faang_closer_positions_1d.csv +++ /dev/null @@ -1,499 +0,0 @@ -date,nflx,amzn,fb,aapl,googl,cash -2016-08-19,-19940.96,-19690.059999999998,-19893.16,19903.52,-19991.25,581.0500000000065 -2016-08-20,-19940.96,-19690.059999999998,-19893.16,19903.52,-19991.25,581.0500000000065 -2016-08-21,-19940.96,-19690.059999999998,-19893.16,19903.52,-19991.25,581.0500000000065 -2016-08-22,-19940.96,19746.48,19988.15,-19965.84,-19991.25,364.0300000000061 -2016-08-23,19955.52,19746.48,19988.15,19919.55,-19923.75,472.88000000000466 -2016-08-24,-19987.800000000003,-19688.5,-20003.760000000002,-19985.55,-19914.75,-57.0199999999968 -2016-08-25,19853.28,19739.72,19822.4,-19985.55,-19840.0,774.6800000000003 -2016-08-26,19853.28,19739.72,19822.4,-19900.449999999997,19830.5,774.6800000000003 -2016-08-27,19906.32,19994.0,19993.6,-19783.899999999998,19830.5,774.6800000000003 -2016-08-28,19906.32,19994.0,19993.6,-19783.899999999998,19830.5,774.6800000000003 -2016-08-29,-20043.8,19994.0,19993.6,-19783.899999999998,19830.5,580.0800000000017 -2016-08-30,20074.7,-19957.08,-20008.56,-19761.699999999997,-19798.0,705.9200000000019 -2016-08-31,20074.7,19998.16,19926.96,19946.8,-19798.0,513.7400000000052 -2016-09-01,-19962.899999999998,19998.16,19926.96,19946.8,19785.0,611.1200000000063 -2016-09-02,-19962.899999999998,20036.12,19934.86,20065.24,19785.0,611.1200000000063 -2016-09-03,-19962.899999999998,20083.440000000002,19988.58,20253.24,19921.75,611.1200000000063 -2016-09-04,-19962.899999999998,20083.440000000002,19988.58,20253.24,19921.75,611.1200000000063 -2016-09-05,-19962.899999999998,20083.440000000002,19988.58,20253.24,19921.75,611.1200000000063 -2016-09-06,20118.09,20083.440000000002,19988.58,-20139.9,19921.75,1119.1800000000076 -2016-09-07,-20424.9,-20396.48,20497.34,20480.04,-20199.75,406.7100000000064 -2016-09-08,20430.3,-20396.48,-20452.390000000003,-20470.88,-20199.75,109.04000000000087 -2016-09-09,-20361.5,-20385.559999999998,-20452.390000000003,-20470.88,-20071.0,-469.9599999999991 -2016-09-10,-20361.5,-19763.64,-19954.7,-20007.219999999998,-19712.0,-469.9599999999991 -2016-09-11,-20361.5,-19763.64,-19954.7,-20007.219999999998,-19712.0,-469.9599999999991 -2016-09-12,19810.0,19287.25,19818.26,19822.72,19171.68,3208.6100000000006 -2016-09-13,-20178.9,-19786.26,-20099.18,19822.72,-19718.0,189.13999999999942 -2016-09-14,19984.06,19788.34,19932.12,20294.600000000002,19761.5,832.7200000000012 -2016-09-15,19984.06,19788.34,19932.12,21012.76,19761.5,832.7200000000012 -2016-09-16,20052.04,20011.940000000002,20022.6,-20455.760000000002,-19949.25,1981.9199999999983 -2016-09-17,20492.88,20241.52,20134.92,-20455.760000000002,-19949.25,1981.9199999999983 -2016-09-18,20492.88,20241.52,20134.92,-20455.760000000002,-19949.25,1981.9199999999983 -2016-09-19,-20592.600000000002,-20152.600000000002,-20584.0,-20455.760000000002,-19949.25,1075.079999999998 -2016-09-20,20436.0,20285.72,-20584.0,-20217.239999999998,19994.5,1271.579999999998 -2016-09-21,-20494.079999999998,20285.72,20530.52,-20215.46,19994.5,772.4200000000019 -2016-09-22,20507.62,20533.24,20530.52,20516.98,20125.75,849.4600000000028 -2016-09-23,20507.62,20922.2,-20729.52,-20738.64,-20374.0,-225.92999999999665 -2016-09-24,20531.16,20949.5,-20729.52,-20738.64,-20374.0,-225.92999999999665 -2016-09-25,20531.16,20949.5,-20729.52,-20738.64,-20374.0,-225.92999999999665 -2016-09-26,-20614.08,-19979.0,-20729.52,20544.16,-20374.0,420.75 -2016-09-27,20384.699999999997,20402.75,20333.02,20544.16,20268.25,1712.0699999999997 -2016-09-28,20384.699999999997,20402.75,20333.02,20582.38,-20251.5,1712.0699999999997 -2016-09-29,-20784.05,20718.0,-20750.58,-20753.300000000003,-20251.5,379.8199999999997 -2016-09-30,20596.95,20726.25,20651.47,20688.149999999998,20101.5,1325.4900000000016 -2016-10-01,20596.95,20932.75,20651.47,20688.149999999998,20101.5,1325.4900000000016 -2016-10-02,20596.95,20932.75,20651.47,20688.149999999998,20101.5,1325.4900000000016 -2016-10-03,20596.95,-20081.760000000002,20651.47,-20816.2,-20809.88,1136.8099999999977 -2016-10-04,-20979.7,-20081.760000000002,-20894.97,20905.0,20872.54,1289.7899999999972 -2016-10-05,20937.16,20264.64,20940.61,20905.0,-20831.98,2140.029999999999 -2016-10-06,-21119.07,-21041.5,20940.61,20914.25,20880.08,878.0899999999965 -2016-10-07,-21119.07,-21041.5,20984.620000000003,21069.65,-20818.46,878.0899999999965 -2016-10-08,-21068.82,-20985.75,21025.370000000003,21101.100000000002,-20818.46,878.0899999999965 -2016-10-09,-21068.82,-20985.75,21025.370000000003,21101.100000000002,-20818.46,878.0899999999965 -2016-10-10,-21068.82,21042.75,21025.370000000003,21101.100000000002,21168.42,878.0899999999965 -2016-10-11,-20769.329999999998,-20775.0,-21265.2,21469.25,-21048.82,620.3299999999945 -2016-10-12,-20218.59,20852.25,21035.15,21515.5,20294.25,1690.199999999997 -2016-10-13,21048.3,-20732.0,-21090.3,-21056.4,-20906.08,313.30999999999403 -2016-10-14,21048.3,-20732.0,20972.32,20938.14,20919.600000000002,676.4499999999935 -2016-10-15,21308.7,-20574.0,20972.32,20938.14,20919.600000000002,676.4499999999935 -2016-10-16,21308.7,-20574.0,20972.32,20938.14,20919.600000000002,676.4499999999935 -2016-10-17,-21057.8,-20574.0,-21044.100000000002,-21041.45,20919.600000000002,331.5599999999904 -2016-10-18,20907.04,20441.25,20828.34,-21041.45,20977.84,4874.919999999991 -2016-10-19,20907.04,20441.25,20828.34,-21027.13,21358.74,4874.919999999991 -2016-10-20,21449.120000000003,-21878.640000000003,-21970.0,-20964.48,-21362.38,2344.279999999988 -2016-10-21,21709.6,21293.74,21923.62,-20953.74,21425.559999999998,3559.479999999985 -2016-10-22,22440.0,21293.74,21923.62,-20871.399999999998,21425.559999999998,3559.479999999985 -2016-10-23,22440.0,21293.74,21923.62,-20871.399999999998,21425.559999999998,3559.479999999985 -2016-10-24,-22282.75,21293.74,21923.62,22235.850000000002,21425.559999999998,2510.309999999983 -2016-10-25,-22282.75,-21714.68,-22489.3,22235.850000000002,-22370.85,1152.599999999984 -2016-10-26,22346.72,-21714.68,-22489.3,-22424.46,-22370.85,447.67999999998574 -2016-10-27,-22132.25,-21387.34,-22276.8,-22424.46,-22196.7,574.1499999999869 -2016-10-28,22023.18,-21277.36,22056.719999999998,-22209.12,21308.559999999998,1782.8599999999897 -2016-10-29,22023.18,-20184.32,22056.719999999998,-22061.68,21308.559999999998,1782.8599999999897 -2016-10-30,22023.18,-20184.32,22056.719999999998,-22061.68,21308.559999999998,1782.8599999999897 -2016-10-31,-21852.25,21325.140000000003,-21875.33,-22061.68,-21867.3,189.25999999998385 -2016-11-01,-21852.25,-21206.07,-21875.33,-22026.760000000002,-21867.3,189.25999999998385 -2016-11-02,-21577.5,-21206.07,-21626.5,21536.87,-21747.96,300.849999999984 -2016-11-03,-21409.5,20709.809999999998,-21237.39,-21197.19,-21287.34,300.849999999984 -2016-11-04,-21374.5,-20386.35,20889.75,-21197.19,-21119.13,-423.650000000016 -2016-11-05,-21355.25,-20386.35,20889.75,-21006.12,-21089.7,-423.650000000016 -2016-11-06,-21355.25,-20386.35,20889.75,-21006.12,-21089.7,-423.650000000016 -2016-11-07,20804.86,20408.18,20889.75,20757.079999999998,20852.78,2711.999999999982 -2016-11-08,-21262.14,20408.18,21131.95,20757.079999999998,20852.78,2214.6399999999812 -2016-11-09,-21262.14,-20840.76,-21433.32,-21399.84,-20945.34,765.1799999999821 -2016-11-10,-20894.489999999998,-20840.76,-21433.32,-21399.84,-20945.34,765.1799999999821 -2016-11-11,-19736.82,-20044.26,-21019.2,20493.27,-20287.54,1198.8999999999833 -2016-11-12,-19627.38,-19953.27,-20709.48,20493.27,-20065.5,1198.8999999999833 -2016-11-13,-19627.38,-19953.27,-20709.48,20493.27,-20065.5,1198.8999999999833 -2016-11-14,-19627.38,-19953.27,-20709.48,-20402.03,-20065.5,776.0599999999831 -2016-11-15,19878.25,19324.24,19806.8,19815.35,19379.0,3282.9799999999814 -2016-11-16,19878.25,19324.24,-20243.16,19815.35,19379.0,2701.2799999999806 -2016-11-17,-20360.31,19408.74,20377.670000000002,-20450.7,19499.5,2479.0599999999795 -2016-11-18,20507.379999999997,19666.399999999998,-20595.52,20581.22,-20175.22,1126.7599999999766 -2016-11-19,20507.379999999997,19764.16,-20595.52,20581.22,-20175.22,1126.7599999999766 -2016-11-20,20507.379999999997,19764.16,-20595.52,20581.22,-20175.22,1126.7599999999766 -2016-11-21,20507.379999999997,19764.16,20457.36,20581.22,20404.8,2100.9199999999764 -2016-11-22,20996.879999999997,20280.0,-21014.31,20893.510000000002,20404.8,1493.5699999999742 -2016-11-23,-20948.82,-20283.12,-21014.31,-21022.47,-21033.0,492.1099999999751 -2016-11-24,-20948.82,-20283.12,-20905.32,-21022.47,-21033.0,492.1099999999751 -2016-11-25,-20948.82,20289.62,-20905.32,20904.73,20285.98,1495.9199999999764 -2016-11-26,-20898.98,20289.62,-20825.739999999998,20904.73,20285.98,1495.9199999999764 -2016-11-27,-20898.98,20289.62,-20825.739999999998,20904.73,20285.98,1495.9199999999764 -2016-11-28,-20898.98,-20702.79,20830.93,-20863.59,20285.98,729.149999999976 -2016-11-29,20799.27,-20702.79,20830.93,-20863.59,20430.54,846.659999999978 -2016-11-30,-20826.0,-20588.04,-20841.920000000002,-20843.02,-20172.88,374.399999999976 -2016-12-01,20513.5,-20265.390000000003,-20841.920000000002,-20667.239999999998,-20172.88,726.0599999999758 -2016-12-02,20513.5,-20078.55,20310.4,20331.5,19875.96,945.8599999999751 -2016-12-03,21141.75,-19989.18,20310.4,20331.5,19875.96,945.8599999999751 -2016-12-04,21141.75,-19989.18,20310.4,20331.5,19875.96,945.8599999999751 -2016-12-05,-20495.52,20502.72,20310.4,-20512.68,19875.96,976.0099999999729 -2016-12-06,20554.05,20502.72,-20646.56,20670.600000000002,-20180.68,1847.999999999971 -2016-12-07,20554.05,20647.440000000002,20877.15,20670.600000000002,20578.22,1730.0499999999702 -2016-12-08,-21074.04,-20717.91,20877.15,20873.64,20578.22,990.6099999999678 -2016-12-09,-21074.04,20753.82,21047.07,21078.56,20674.42,990.6099999999678 -2016-12-10,-21012.48,20753.82,21183.36,21422.600000000002,21045.7,990.6099999999678 -2016-12-11,-21012.48,20753.82,21183.36,21422.600000000002,21045.7,990.6099999999678 -2016-12-12,-21012.48,-20523.24,-21198.6,-21187.1,-21005.399999999998,750.5999999999694 -2016-12-13,21042.6,20907.18,21054.25,21079.77,20383.5,2752.0299999999697 -2016-12-14,-21355.12,-20758.140000000003,-21397.379999999997,21079.77,20383.5,2021.0799999999726 -2016-12-15,21375.0,-20758.140000000003,21340.89,21079.77,-21206.899999999998,1575.9999999999745 -2016-12-16,-21365.84,-20547.0,-21336.86,21195.059999999998,-21206.899999999998,1331.9099999999744 -2016-12-17,-21365.84,-20459.79,-21336.86,21222.51,-21055.84,1331.9099999999744 -2016-12-18,-21365.84,-20459.79,-21336.86,21222.51,-21055.84,1331.9099999999744 -2016-12-19,21326.5,20682.0,-21336.86,21222.51,21125.0,1582.8099999999758 -2016-12-20,-21395.52,20682.0,-21224.719999999998,21345.12,21125.0,1457.6899999999769 -2016-12-21,21378.5,-20806.2,-21198.02,21401.850000000002,-21117.2,1710.6899999999769 -2016-12-22,-21474.18,-20806.2,-21189.120000000003,-21513.65,-21117.2,1226.9499999999753 -2016-12-23,21350.3,-20691.18,-20897.2,21323.16,-21051.68,1585.5799999999763 -2016-12-24,21350.3,-20535.93,-20874.059999999998,21323.16,-21002.8,1585.5799999999763 -2016-12-25,21350.3,-20535.93,-20874.059999999998,21323.16,-21002.8,1585.5799999999763 -2016-12-26,21350.3,-20535.93,-20874.059999999998,21323.16,-21002.8,1585.5799999999763 -2016-12-27,21350.3,20827.8,21241.8,21323.16,21058.18,1349.5599999999795 -2016-12-28,-21527.19,20827.8,-21513.28,-21483.84,-20918.82,639.2299999999814 -2016-12-29,-21527.19,-20659.05,-21513.28,-21483.84,-20918.82,639.2299999999814 -2016-12-30,-21431.43,-20659.05,-21408.399999999998,-21478.32,-20874.88,639.2299999999814 -2016-12-31,-21169.8,-20246.49,-21169.2,-21310.879999999997,-20603.7,639.2299999999814 -2017-01-01,-21169.8,-20246.49,-21169.2,-21310.879999999997,-20603.7,639.2299999999814 -2017-01-02,-21169.8,-20246.49,-21169.2,-21310.879999999997,-20603.7,639.2299999999814 -2017-01-03,20908.36,20349.09,20917.94,21023.15,21008.26,2464.4099999999853 -2017-01-04,20908.36,20349.09,20917.94,-21231.66,-21002.02,2232.3699999999844 -2017-01-05,21223.239999999998,20443.859999999997,21245.51,21456.24,21138.52,2115.759999999984 -2017-01-06,-21757.62,21072.15,21599.93,21456.24,21138.52,1853.6199999999844 -2017-01-07,-21757.62,21491.73,22090.39,21695.44,21455.46,1853.6199999999844 -2017-01-08,-21757.62,21491.73,22090.39,21695.44,21455.46,1853.6199999999844 -2017-01-09,-21757.62,21491.73,22090.39,21695.44,21455.46,1853.6199999999844 -2017-01-10,-21737.699999999997,-21489.3,-22134.3,21894.16,-21476.26,1977.969999999983 -2017-01-11,22054.5,21573.54,22065.75,21916.24,21576.36,1964.7399999999834 -2017-01-12,-22218.960000000003,21573.54,22065.75,-22180.5,-21567.78,1338.6999999999825 -2017-01-13,22194.199999999997,21968.28,22158.5,-22180.5,21604.440000000002,2140.8999999999833 -2017-01-14,22194.199999999997,22062.78,22459.5,-22141.440000000002,21604.440000000002,2140.8999999999833 -2017-01-15,22194.199999999997,22062.78,22459.5,-22141.440000000002,21604.440000000002,2140.8999999999833 -2017-01-16,22194.199999999997,22062.78,22459.5,-22141.440000000002,21604.440000000002,2140.8999999999833 -2017-01-17,-22458.409999999996,-21862.440000000002,-22505.120000000003,22440.0,-22341.420000000002,666.8999999999796 -2017-01-18,22387.68,-21862.440000000002,22386.0,-22438.129999999997,22383.54,928.0799999999799 -2017-01-19,22387.68,21844.079999999998,-22448.8,-22438.129999999997,-22257.99,800.5299999999806 -2017-01-20,23252.88,-21824.91,-22448.8,22560.0,22360.59,680.5299999999806 -2017-01-21,23284.8,-21824.91,-22359.04,22560.0,22360.59,680.5299999999806 -2017-01-22,23284.8,-21824.91,-22359.04,22560.0,22360.59,680.5299999999806 -2017-01-23,-22531.96,22082.76,22562.75,22560.0,22360.59,1359.0199999999786 -2017-01-24,22697.820000000003,22082.76,22562.75,-22674.329999999998,22799.609999999997,1519.2699999999786 -2017-01-25,-22881.280000000002,22205.88,22639.75,22913.44,22937.309999999998,1362.1099999999788 -2017-01-26,-22881.280000000002,22586.04,23009.0,22913.44,-23138.46,1362.1099999999788 -2017-01-27,23219.35,-22565.79,-23131.5,22924.72,-23138.46,1504.5599999999795 -2017-01-28,23219.35,-22565.79,-23131.5,22926.600000000002,-22815.809999999998,1504.5599999999795 -2017-01-29,23219.35,-22565.79,-23131.5,22926.600000000002,-22815.809999999998,1504.5599999999795 -2017-01-30,-23160.079999999998,-22565.79,-23131.5,-23231.329999999998,-22815.809999999998,998.4499999999825 -2017-01-31,-23160.079999999998,-22420.26,-22921.5,-23231.329999999998,-22243.41,998.4499999999825 -2017-02-01,22806.36,22473.45,22782.329999999998,22788.75,-22145.13,3615.4299999999857 -2017-02-02,-23246.399999999998,22473.45,-23289.52,-23263.93,22911.28,671.1699999999873 -2017-02-03,23141.25,-22685.600000000002,23183.46,23105.320000000003,22911.28,530.609999999986 -2017-02-04,23141.25,-22685.600000000002,23183.46,23105.320000000003,22963.64,530.609999999986 -2017-02-05,23141.25,-22685.600000000002,23183.46,23105.320000000003,22963.64,530.609999999986 -2017-02-06,23141.25,-22685.600000000002,23183.46,23105.320000000003,22963.64,530.609999999986 -2017-02-07,23260.05,22750.0,-23203.84,23321.91,23005.36,662.4499999999862 -2017-02-08,23760.0,22750.0,23350.8,23543.87,23218.440000000002,930.849999999984 -2017-02-09,-23494.819999999996,22951.88,-23474.499999999996,23635.16,23236.64,1084.989999999987 -2017-02-10,23460.84,22998.08,23483.25,-23517.36,23241.68,1361.9299999999857 -2017-02-11,23460.84,23168.88,23483.25,-23517.36,23375.8,1361.9299999999857 -2017-02-12,23460.84,23168.88,23483.25,-23517.36,23375.8,1361.9299999999857 -2017-02-13,-23627.999999999996,23168.88,-23592.800000000003,23592.329999999998,23375.8,931.5699999999888 -2017-02-14,-23627.999999999996,-23418.92,-23592.800000000003,23592.329999999998,23490.88,931.5699999999888 -2017-02-15,23616.820000000003,23595.600000000002,-23557.6,23898.54,-23444.960000000003,789.2999999999884 -2017-02-16,-23715.67,23595.600000000002,23689.68,-23685.375,23580.76,784.1399999999885 -2017-02-17,23750.74,23635.92,-23768.34,23751.0,23580.76,650.6099999999897 -2017-02-18,23750.74,23661.960000000003,-23768.34,23751.0,23703.399999999998,650.6099999999897 -2017-02-19,23750.74,23661.960000000003,-23768.34,23751.0,23703.399999999998,650.6099999999897 -2017-02-20,23750.74,23661.960000000003,-23768.34,23751.0,23703.399999999998,650.6099999999897 -2017-02-21,23750.74,23661.960000000003,23802.16,23751.0,23703.399999999998,650.6099999999897 -2017-02-22,23814.2,-23957.08,23802.16,23922.499999999996,23779.559999999998,650.6099999999897 -2017-02-23,-24129.82,-23957.08,-24094.08,-24029.28,-23828.0,228.51999999998952 -2017-02-24,23922.75,-23861.32,23972.88,23915.5,-23828.0,787.1199999999881 -2017-02-25,23922.75,-23666.72,23972.88,23915.5,-23738.68,787.1199999999881 -2017-02-26,23922.75,-23666.72,23972.88,23915.5,-23738.68,787.1199999999881 -2017-02-27,23922.75,23761.92,23972.88,23915.5,23790.76,787.1199999999881 -2017-02-28,-24019.969999999998,-23661.12,-23990.579999999998,23962.75,-23658.039999999997,502.85999999998967 -2017-03-01,23822.55,23886.24,23911.079999999998,23973.25,23132.25,2057.169999999991 -2017-03-02,-24138.69,-23769.48,-24206.519999999997,-24179.04,-23795.8,98.81999999999243 -2017-03-03,-24138.69,23796.64,24004.749999999996,23902.38,-23795.8,792.4999999999927 -2017-03-04,-24071.219999999998,23796.64,24004.749999999996,23902.38,-23774.24,792.4999999999927 -2017-03-05,-24071.219999999998,23796.64,24004.749999999996,23902.38,-23774.24,792.4999999999927 -2017-03-06,23987.86,-23705.08,24004.749999999996,-23966.48,-23774.24,1220.919999999991 -2017-03-07,-24043.100000000002,-23705.08,-24027.500000000004,23997.440000000002,23832.2,1079.4899999999907 -2017-03-08,-24043.100000000002,23814.0,24101.0,-24047.0,23832.2,940.4899999999907 -2017-03-09,24030.63,23814.0,24101.0,-24047.0,23901.92,799.9599999999882 -2017-03-10,24030.63,-23868.88,24192.0,24071.219999999998,24019.52,799.9599999999882 -2017-03-11,24092.19,-23868.88,24288.25,24071.219999999998,24119.34,799.9599999999882 -2017-03-12,24092.19,-23868.88,24288.25,24071.219999999998,24119.34,799.9599999999882 -2017-03-13,24092.19,23928.52,24288.25,24071.219999999998,24119.34,799.9599999999882 -2017-03-14,-24342.3,-23870.84,-24381.0,-24323.25,24208.24,665.1699999999873 -2017-03-15,24256.75,23883.16,24311.28,24299.58,24245.48,1521.5599999999868 -2017-03-16,-24401.909999999996,23883.16,24311.28,24299.58,24314.92,1232.779999999988 -2017-03-17,24378.480000000003,-23864.68,-24472.0,-24498.25,24360.0,958.0699999999888 -2017-03-18,24378.480000000003,-23864.68,-24472.0,-24498.25,24426.36,958.0699999999888 -2017-03-19,24378.480000000003,-23864.68,-24472.0,-24498.25,24426.36,958.0699999999888 -2017-03-20,24378.480000000003,23995.16,24489.5,24472.58,-24301.48,1240.989999999987 -2017-03-21,-24496.239999999998,-24452.800000000003,-24516.269999999997,-24472.0,-24301.48,-728.5900000000111 -2017-03-22,24107.850000000002,23745.68,24149.07,24182.819999999996,-23803.92,1671.4599999999846 -2017-03-23,-24254.64,-23726.64,-24278.22,-24238.239999999998,-23794.399999999998,1107.32999999998 -2017-03-24,24143.4,-23726.64,24138.48,-24238.239999999998,-23510.2,1530.0299999999806 -2017-03-25,24143.4,-23677.08,24138.48,-24190.079999999998,-23383.92,1530.0299999999806 -2017-03-26,24143.4,-23677.08,24138.48,-24190.079999999998,-23383.92,1530.0299999999806 -2017-03-27,24143.4,23710.960000000003,-24135.039999999997,24090.48,23478.28,1670.9099999999817 -2017-03-28,24490.2,23710.960000000003,24240.96,24090.48,23478.28,1812.66999999998 -2017-03-29,24678.899999999998,23968.0,24240.96,24589.800000000003,23537.64,1812.66999999998 -2017-03-30,24899.9,24480.960000000003,-24779.34,-24755.960000000003,-24634.920000000002,392.02999999998065 -2017-03-31,-24832.08,24537.52,-24779.34,-24755.960000000003,-24634.920000000002,687.6499999999796 -2017-04-01,-24832.08,24823.12,-24716.7,-24709.52,-24586.199999999997,687.6499999999796 -2017-04-02,-24832.08,24823.12,-24716.7,-24709.52,-24586.199999999997,687.6499999999796 -2017-04-03,-24832.08,24823.12,24756.72,24860.1,24845.75,543.9499999999789 -2017-04-04,-24682.559999999998,24962.28,-24802.75,24860.1,-24724.530000000002,402.2199999999757 -2017-04-05,-24444.0,25391.24,24823.75,-24915.460000000003,-24724.530000000002,402.2199999999757 -2017-04-06,24867.02,-24253.559999999998,-24845.92,-24915.460000000003,-24618.39,440.62999999997555 -2017-04-07,-24614.920000000002,-24253.559999999998,-24845.92,-24853.18,-24507.755,583.7399999999761 -2017-04-08,-24614.920000000002,-24161.76,-24777.28,-24797.82,-24420.9,583.7399999999761 -2017-04-09,-24614.920000000002,-24161.76,-24777.28,-24797.82,-24420.9,583.7399999999761 -2017-04-10,24598.35,24490.079999999998,24540.96,-24797.82,-24420.9,1009.6699999999764 -2017-04-11,24598.35,-24363.72,-24625.92,-24768.409999999996,-24409.300000000003,729.8299999999763 -2017-04-12,-24594.930000000004,-24363.72,-24625.92,24531.4,24402.34,729.8299999999763 -2017-04-13,-24594.930000000004,-24198.21,-24566.08,-24542.7,-24365.219999999998,588.779999999977 -2017-04-14,-24439.319999999996,-23886.09,-24532.64,-24542.7,-24365.219999999998,588.779999999977 -2017-04-15,-24439.319999999996,-23886.09,-24532.64,-24542.7,-24365.219999999998,588.779999999977 -2017-04-16,-24439.319999999996,-23886.09,-24532.64,-24542.7,-24365.219999999998,588.779999999977 -2017-04-17,24443.5,24353.73,24465.659999999996,24394.760000000002,23943.64,2888.07999999998 -2017-04-18,-24801.280000000002,24353.73,-24808.960000000003,-24851.199999999997,-24765.71,42.88999999998123 -2017-04-19,-24801.280000000002,-24278.4,24612.710000000003,-24851.199999999997,23982.28,1326.209999999981 -2017-04-20,24565.32,24355.62,24612.710000000003,24499.68,23982.28,1754.789999999979 -2017-04-21,24565.32,-24260.309999999998,-24712.960000000003,-24754.980000000003,-24050.600000000002,1613.9299999999748 -2017-04-22,24859.38,-24260.309999999998,-24712.960000000003,-24754.980000000003,-24050.600000000002,1613.9299999999748 -2017-04-23,24859.38,-24260.309999999998,-24712.960000000003,-24754.980000000003,-24050.600000000002,1613.9299999999748 -2017-04-24,24859.38,24500.07,24729.9,24849.719999999998,24610.039999999997,2048.509999999973 -2017-04-25,25026.420000000002,24500.07,24729.9,24849.719999999998,24610.039999999997,2048.509999999973 -2017-04-26,-25528.899999999998,24505.74,24903.300000000003,-25426.191600000002,24887.52,2074.586799999972 -2017-04-27,25411.280000000002,24550.829999999998,24915.2,25450.829999999998,24895.92,2686.9067999999716 -2017-04-28,-25569.6,24806.52,25108.999999999996,-25569.7,24960.32,2238.8567999999686 -2017-04-29,-25569.6,24974.73,25542.5,-25569.7,25886.559999999998,2238.8567999999686 -2017-04-30,-25569.6,24974.73,25542.5,-25569.7,25886.559999999998,2238.8567999999686 -2017-05-01,25943.45,24974.73,25542.5,25948.2,25886.559999999998,2540.8067999999657 -2017-05-02,25943.45,-25560.359999999997,25918.2,25948.2,26118.960000000003,2540.8067999999657 -2017-05-03,-26450.3,-25560.359999999997,-26413.2,-26470.8,26238.52,1025.656799999968 -2017-05-04,26418.0,-25407.809999999998,-26413.2,-26470.8,26556.600000000002,1340.156799999968 -2017-05-05,-26465.399999999998,-25313.309999999998,-26247.899999999998,26365.920000000002,-25657.559999999998,2580.716799999973 -2017-05-06,-26465.399999999998,-25222.05,-26141.760000000002,26365.920000000002,-25657.559999999998,2580.716799999973 -2017-05-07,-26465.399999999998,-25222.05,-26141.760000000002,26365.920000000002,-25657.559999999998,2580.716799999973 -2017-05-08,-26465.399999999998,25624.079999999998,26435.5,26365.920000000002,25884.63,2429.6567999999716 -2017-05-09,26768.2,25624.079999999998,-26634.96,27081.0,-25831.170000000002,1971.2367999999733 -2017-05-10,26768.2,-26570.600000000002,-26634.96,-26820.5,-25831.170000000002,1328.8067999999694 -2017-05-11,-26793.26,-26570.600000000002,-26601.329999999998,26787.3,26764.92,685.4067999999679 -2017-05-12,26694.46,25956.45,26758.74,26787.3,-26743.92,1978.8567999999686 -2017-05-13,26694.46,25956.45,26758.74,27161.399999999998,-26743.92,1978.8567999999686 -2017-05-14,26694.46,25956.45,26758.74,27161.399999999998,-26743.92,1978.8567999999686 -2017-05-15,-27043.38,-26823.16,-27034.2,-26936.1,26858.16,396.14679999997315 -2017-05-16,-27043.38,26083.890000000003,-27034.2,-26936.1,26858.16,1362.2167999999729 -2017-05-17,-26940.29,-26446.559999999998,-26960.4,-26896.31,-26380.76,417.69679999997607 -2017-05-18,26157.6,25879.23,26135.82,26236.879999999997,25663.5,3077.906799999979 -2017-05-19,26157.6,25879.23,26135.82,26236.879999999997,25663.5,3077.906799999979 -2017-05-20,26379.36,25915.68,26206.62,26309.120000000003,25775.55,3077.906799999979 -2017-05-21,26379.36,25915.68,26206.62,26309.120000000003,25775.55,3077.906799999979 -2017-05-22,26379.36,25915.68,26206.62,26309.120000000003,25775.55,3077.906799999979 -2017-05-23,26402.88,26208.9,-26800.67,-26761.2,26029.890000000003,2178.0267999999814 -2017-05-24,-26817.5,26231.579999999998,26857.16,-26761.2,26204.85,2162.6067999999796 -2017-05-25,27066.300000000003,26469.45,26857.16,26927.25,26395.47,2660.9367999999813 -2017-05-26,-27450.670000000002,26821.26,27200.84,-27342.58,26780.22,1712.8167999999823 -2017-05-27,-27450.670000000002,26886.059999999998,27231.27,-27342.58,26818.29,1712.8167999999823 -2017-05-28,-27450.670000000002,26886.059999999998,27231.27,-27342.58,26818.29,1712.8167999999823 -2017-05-29,-27450.670000000002,26886.059999999998,27231.27,-27342.58,26818.29,1712.8167999999823 -2017-05-30,27420.96,26886.059999999998,27231.27,27353.26,26818.29,1876.0367999999835 -2017-05-31,-27395.76,-26854.74,-27414.260000000002,-27496.8,-26651.43,1267.5967999999812 -2017-06-01,-27395.76,26890.65,27275.4,27266.04,26683.829999999998,1725.4867999999806 -2017-06-02,27419.88,26890.65,27275.4,27266.04,26683.829999999998,2055.846799999981 -2017-06-03,27419.88,27181.71,27649.800000000003,27670.1,26895.24,2055.846799999981 -2017-06-04,27419.88,27181.71,27649.800000000003,27670.1,26895.24,2055.846799999981 -2017-06-05,-27730.08,27181.71,27649.800000000003,-27707.4,26895.24,1417.866799999978 -2017-06-06,27748.559999999998,-27080.190000000002,-27658.61,27646.55,-26910.359999999997,1419.5067999999737 -2017-06-07,27748.559999999998,27271.890000000003,27561.600000000002,27646.55,27042.93,1572.6267999999727 -2017-06-08,27822.480000000003,-27268.38,27561.600000000002,-27743.210000000003,27042.93,1572.6267999999727 -2017-06-09,-27813.28,-27268.38,-27831.18,-27743.210000000003,-27163.36,-1559.5132000000267 -2017-06-10,-27813.28,-26414.37,-27831.18,-26667.42,-27163.36,-1559.5132000000267 -2017-06-11,-27813.28,-26414.37,-27831.18,-26667.42,-27163.36,-1559.5132000000267 -2017-06-12,-27813.28,-26414.37,-27831.18,-26667.42,-27163.36,-1559.5132000000267 -2017-06-13,26267.84,25500.54,26218.32,26239.61,26203.5,2810.8167999999714 -2017-06-14,-26634.999999999996,-26364.690000000002,-26594.25,-26564.28,-26134.109999999997,346.3567999999723 -2017-06-15,-26634.999999999996,-26364.690000000002,-26594.25,-26564.28,-26134.109999999997,346.3567999999723 -2017-06-16,26209.36,25680.46,26211.359999999997,-26405.07,-25924.859999999997,2243.1267999999764 -2017-06-17,26209.36,25680.46,26211.359999999997,-26035.410000000003,-25882.74,2243.1267999999764 -2017-06-18,26209.36,25680.46,26211.359999999997,-26035.410000000003,-25882.74,2243.1267999999764 -2017-06-19,26209.36,25680.46,26211.359999999997,26341.2,26330.940000000002,2682.146799999977 -2017-06-20,-26760.800000000003,-26799.93,-26796.0,-26826.85,-26162.73,51.80679999997665 -2017-06-21,26665.16,26057.98,26626.43,26548.34,26421.93,2573.4967999999753 -2017-06-22,-26950.859999999997,-26033.8,-26845.0,-26941.55,-26368.74,1520.0267999999778 -2017-06-23,26865.1,26097.24,26827.11,26928.399999999998,26624.43,2608.636799999982 -2017-06-24,26865.1,26097.24,26827.11,26928.399999999998,26624.43,2608.636799999982 -2017-06-25,26865.1,26097.24,26827.11,26928.399999999998,26624.43,2608.636799999982 -2017-06-26,-27090.0,-26837.46,-27185.43,-27122.52,-26246.43,393.65679999997883 -2017-06-27,-27090.0,-26837.46,-27185.43,-27122.52,-26246.43,393.65679999997883 -2017-06-28,26233.11,25748.58,26204.04,26249.4,25947.27,3331.8167999999787 -2017-06-29,-26716.02,-26350.109999999997,-26734.079999999998,-26724.48,-26258.960000000003,-1400.8832000000148 -2017-06-30,-26716.02,-26350.109999999997,-26734.079999999998,26211.640000000003,-26258.960000000003,-824.8032000000167 -2017-07-01,-26594.98,-26136.0,-26723.46,26211.640000000003,-26031.039999999997,-824.8032000000167 -2017-07-02,-26594.98,-26136.0,-26723.46,26211.640000000003,-26031.039999999997,-824.8032000000167 -2017-07-03,-26594.98,-26136.0,-26723.46,-26117.0,-26031.039999999997,-824.8032000000167 -2017-07-04,-26018.26,-25748.82,-26272.11,-26117.0,-25744.88,-824.8032000000167 -2017-07-05,25684.140000000003,25256.399999999998,25708.14,25792.11,25171.02,3003.606799999983 -2017-07-06,-26032.5,-26058.78,-26043.5,-26119.589999999997,-25975.32,-640.4232000000156 -2017-07-07,25830.960000000003,25447.76,25896.239999999998,25808.22,25401.87,3362.706799999989 -2017-07-08,25830.960000000003,25447.76,25896.239999999998,25808.22,25401.87,3362.706799999989 -2017-07-09,25830.960000000003,25447.76,25896.239999999998,25808.22,25401.87,3362.706799999989 -2017-07-10,25830.960000000003,25447.76,25896.239999999998,25808.22,25401.87,3362.706799999989 -2017-07-11,26259.239999999998,-25847.38,26248.5,25965.74,25677.0,3362.706799999989 -2017-07-12,26544.760000000002,26169.26,26551.170000000002,26049.87,25745.309999999998,3362.706799999989 -2017-07-13,-27205.239999999998,-26996.085,27171.9,26087.460000000003,26126.82,2362.8517999999895 -2017-07-14,27229.280000000002,27048.87,27233.46,26450.83,26155.372499999998,2846.2117999999864 -2017-07-15,27229.280000000002,27048.87,27354.87,26678.16,26376.57,2846.2117999999864 -2017-07-16,27229.280000000002,27048.87,27354.87,26678.16,26376.57,2846.2117999999864 -2017-07-17,27229.280000000002,27048.87,-27473.559999999998,26678.16,-27326.88,1710.5217999999877 -2017-07-18,27327.3,27271.079999999998,27523.340000000004,26771.24,26647.65,3186.0517999999865 -2017-07-19,31028.399999999998,27658.260000000002,27523.340000000004,26864.320000000003,26647.65,3186.0517999999865 -2017-07-20,-28641.6,27725.489999999998,27739.659999999996,-28564.600000000002,-27781.32,2926.921799999982 -2017-07-21,28658.079999999998,-27693.090000000004,-28610.82,-28564.600000000002,27827.52,2858.931799999984 -2017-07-22,28658.079999999998,-27693.090000000004,-28610.82,-28551.300000000003,27827.52,2858.931799999984 -2017-07-23,28658.079999999998,-27693.090000000004,-28610.82,-28551.300000000003,27827.52,2858.931799999984 -2017-07-24,-28750.23,28051.65,28718.0,28745.010000000002,27827.52,2989.111799999984 -2017-07-25,-28750.23,28051.65,-28924.0,28745.010000000002,-28101.87,1689.521799999984 -2017-07-26,28740.160000000003,28076.489999999998,28816.140000000003,28867.86,-28101.87,2044.2117999999828 -2017-07-27,-28863.440000000002,-28242.0,28816.140000000003,-28907.52,-27993.989999999998,496.4517999999807 -2017-07-28,28710.239999999998,-28242.0,29656.56,-28907.52,28749.9,-93.79820000001928 -2017-07-29,28710.239999999998,-27541.079999999998,30006.3,-28704.0,28749.9,-93.79820000001928 -2017-07-30,28710.239999999998,-27541.079999999998,30006.3,-28704.0,28749.9,-93.79820000001928 -2017-07-31,-28702.28,-27541.079999999998,-28603.25,-28704.0,-28365.0,389.131799999981 -2017-08-01,28214.65,27893.32,28196.760000000002,28209.4,27450.239999999998,1995.3717999999863 -2017-08-02,-28376.18,-27884.92,-28264.75,28209.4,27450.239999999998,1464.6417999999867 -2017-08-03,-28376.18,-27884.92,-28264.75,-28469.309999999998,-28209.0,1302.191799999986 -2017-08-04,28302.390000000003,27652.24,28326.54,28306.589999999997,28373.699999999997,1614.9717999999884 -2017-08-05,28302.390000000003,27652.24,28326.54,28306.589999999997,28373.699999999997,1614.9717999999884 -2017-08-06,28302.390000000003,27652.24,28326.54,28306.589999999997,28373.699999999997,1614.9717999999884 -2017-08-07,28302.390000000003,27652.24,28326.54,28306.589999999997,-28372.5,1614.9717999999884 -2017-08-08,-28537.600000000002,27652.24,-28595.41,28306.589999999997,-28372.5,1079.8917999999867 -2017-08-09,-28537.600000000002,-28478.29,-28595.41,28974.480000000003,-28325.7,97.88179999998465 -2017-08-10,-28124.8,-28478.29,-28587.06,-28414.410000000003,-28202.4,-212.65820000001622 -2017-08-11,27595.4,27103.72,27733.2,27716.48,26972.61,2952.5417999999772 -2017-08-12,27595.4,27103.72,27733.2,27716.48,26972.61,2952.5417999999772 -2017-08-13,27595.4,27103.72,27733.2,27716.48,26972.61,2952.5417999999772 -2017-08-14,-27873.0,27103.72,27733.2,27716.48,26972.61,2610.5417999999772 -2017-08-15,-27873.0,-27516.72,28173.75,28133.6,-28142.4,1672.4617999999755 -2017-08-16,28216.679999999997,-27516.72,-28220.0,-28166.249999999996,27383.829999999998,2097.741799999978 -2017-08-17,-28235.3,-27389.039999999997,-28220.0,-28166.249999999996,-27829.8,505.72179999998116 -2017-08-18,27645.64,-26895.960000000003,27622.649999999998,-27627.25,-27829.8,1339.2917999999809 -2017-08-19,27645.64,-26837.16,27622.649999999998,-27562.5,-27785.399999999998,1339.2917999999809 -2017-08-20,27645.64,-26837.16,27622.649999999998,-27562.5,-27785.399999999998,1339.2917999999809 -2017-08-21,27645.64,-26837.16,27622.649999999998,-27562.5,-27785.399999999998,1339.2917999999809 -2017-08-22,27682.16,27073.2,27683.7,27641.94,27271.6,2599.2517999999836 -2017-08-23,-28063.96,-27782.0,-28005.86,27641.94,27271.6,1472.5417999999845 -2017-08-24,-28063.96,-27782.0,-28005.86,-28031.52,-27169.81,994.7317999999868 -2017-08-25,-27909.579999999998,-27621.050000000003,-27844.84,27815.640000000003,-27169.81,1314.4517999999844 -2017-08-26,-27547.699999999997,-27412.54,-27609.12,27815.640000000003,-26984.5,1314.4517999999844 -2017-08-27,-27547.699999999997,-27412.54,-27609.12,27815.640000000003,-26984.5,1314.4517999999844 -2017-08-28,27574.8,27434.579999999998,27594.600000000002,27815.640000000003,-26984.5,1648.811799999985 -2017-08-29,27574.8,27434.579999999998,27594.600000000002,28095.78,27136.75,1648.811799999985 -2017-08-30,27853.65,27667.739999999998,27728.250000000004,28346.34,27136.75,1648.811799999985 -2017-08-31,28823.85,28060.11,28036.8,28422.899999999998,27365.27,1648.811799999985 -2017-09-01,28827.15,-28369.25,28380.0,28536.0,-28559.7,696.8217999999833 -2017-09-02,28832.100000000002,-28369.25,28383.300000000003,28544.7,-28559.7,696.8217999999833 -2017-09-03,28832.100000000002,-28369.25,28383.300000000003,28544.7,-28559.7,696.8217999999833 -2017-09-04,28832.100000000002,-28369.25,28383.300000000003,28544.7,-28559.7,696.8217999999833 -2017-09-05,-28621.280000000002,-28369.25,-28510.24,-28526.08,-28559.7,205.7417999999816 -2017-09-06,28321.5,28066.199999999997,28394.850000000002,-28526.08,28260.6,1625.4217999999782 -2017-09-07,-28461.0,28066.199999999997,28394.850000000002,-28496.16,28260.6,1446.4217999999782 -2017-09-08,-28461.0,-28011.1,-28719.6,-28381.76,-28242.3,933.5717999999761 -2017-09-09,-28050.78,-28011.1,-28719.6,-27918.879999999997,-28242.3,933.5717999999761 -2017-09-10,-28050.78,-28011.1,-28719.6,-27918.879999999997,-28242.3,933.5717999999761 -2017-09-11,28351.440000000002,28360.84,28282.129999999997,28262.5,28298.699999999997,2507.8417999999765 -2017-09-12,28351.440000000002,28360.84,-28711.36,-28786.78,28298.699999999997,1345.6817999999766 -2017-09-13,-28831.48,28494.82,28899.350000000002,-28786.78,28399.5,988.9917999999743 -2017-09-14,-28831.48,-28774.09,-28892.24,-28577.350000000002,-28203.9,647.0717999999724 -2017-09-15,-28672.91,-28774.09,28653.86,28618.52,-28203.9,990.2317999999723 -2017-09-16,-28589.7,-28616.91,28653.86,28618.52,-28058.699999999997,990.2317999999723 -2017-09-17,-28589.7,-28616.91,28653.86,28618.52,-28058.699999999997,990.2317999999723 -2017-09-18,28616.100000000002,-28616.91,-28561.68,-28560.6,-28058.699999999997,1030.7917999999663 -2017-09-19,28616.100000000002,-28251.510000000002,28465.800000000003,28571.399999999998,28105.8,1548.351799999964 -2017-09-20,-28568.539999999997,28223.09,-28580.219999999998,-28716.879999999997,28105.8,937.4117999999689 -2017-09-21,28505.78,-27974.85,-28580.219999999998,-28716.879999999997,28426.199999999997,1503.751799999969 -2017-09-22,-28477.2,-27974.85,-28404.260000000002,-28223.76,-28297.8,1316.401799999967 -2017-09-23,-28477.2,-27697.9,-28309.64,-27947.76,-28297.8,1316.401799999967 -2017-09-24,-28477.2,-27697.9,-28309.64,-27947.76,-28297.8,1316.401799999967 -2017-09-25,-28477.2,-27697.9,-28309.64,-27947.76,-28297.8,1316.401799999967 -2017-09-26,27624.52,-27253.91,27587.280000000002,27565.199999999997,27185.469999999998,2179.2117999999646 -2017-09-27,27624.52,27575.23,27587.280000000002,27565.199999999997,27185.469999999998,2179.2117999999646 -2017-09-28,-28189.199999999997,27575.23,28170.24,-28203.52,27837.1,1204.6917999999678 -2017-09-29,28290.6,27735.6,28346.64,28203.96,27979.489999999998,1358.8117999999704 -2017-09-30,28290.6,27879.15,28696.08,28203.96,28237.88,1358.8117999999704 -2017-10-01,28290.6,27879.15,28696.08,28203.96,28237.88,1358.8117999999704 -2017-10-02,-28498.609999999997,-27816.510000000002,-28470.96,-28454.850000000002,-28056.63,166.1417999999685 -2017-10-03,28132.829999999998,-27816.510000000002,28213.36,28269.839999999997,28190.32,1531.7817999999752 -2017-10-04,28132.829999999998,27998.050000000003,-28294.559999999998,-28388.397999999997,-28036.62,888.040199999974 -2017-10-05,28958.649999999998,27998.050000000003,28425.84,28436.37,27585.32,2526.4901999999747 -2017-10-06,30519.23,28444.65,28425.84,-29041.100000000002,27585.32,1905.290199999974 -2017-10-07,31089.140000000003,28697.82,28590.179999999997,-29041.100000000002,27821.92,1905.290199999974 -2017-10-08,31089.140000000003,28697.82,28590.179999999997,-29041.100000000002,27821.92,1905.290199999974 -2017-10-09,-29333.63,28697.82,28590.179999999997,29297.920000000002,-28776.989999999998,2332.1001999999717 -2017-10-10,-29333.63,-28628.800000000003,-29341.89,29297.920000000002,-28776.989999999998,1474.1501999999746 -2017-10-11,-29066.920000000002,28855.0,29193.06,29309.2,29163.85,1819.6301999999741 -2017-10-12,29379.000000000004,28855.0,-29333.500000000004,-29484.0,29163.85,1295.2201999999706 -2017-10-13,29379.000000000004,29026.969999999998,29535.800000000003,29514.120000000003,29163.85,1452.2101999999686 -2017-10-14,29923.5,29085.260000000002,29535.800000000003,29514.120000000003,29228.23,1452.2101999999686 -2017-10-15,29923.5,29085.260000000002,29535.800000000003,29514.120000000003,29228.23,1452.2101999999686 -2017-10-16,29923.5,29085.260000000002,29535.800000000003,29514.120000000003,29228.23,1452.2101999999686 -2017-10-17,-29922.0,29183.86,29668.4,30057.44,29271.15,1452.2101999999686 -2017-10-18,-29922.0,-29910.0,-29925.1,-29875.12,29319.0,614.970199999967 -2017-10-19,-29331.0,-29910.0,-29925.1,-29875.12,-29053.36,614.970199999967 -2017-10-20,-29269.5,-29598.3,29396.64,29358.079999999998,29147.030000000002,808.7701999999663 -2017-10-21,-29124.0,-29487.3,29396.64,29358.079999999998,29147.030000000002,808.7701999999663 -2017-10-22,-29124.0,-29487.3,29396.64,29358.079999999998,29147.030000000002,808.7701999999663 -2017-10-23,-29124.0,-29487.3,-29458.440000000002,29358.079999999998,-28580.66,123.69019999996453 -2017-10-24,29010.960000000003,28301.1,29034.2,29359.96,28666.21,2007.0301999999647 -2017-10-25,-29259.27,-29187.3,-29172.6,-29247.735,28666.21,268.01519999996526 -2017-10-26,29086.29,-29187.3,29007.1,29120.85,-28751.18,1143.8851999999679 -2017-10-27,29086.29,28624.7,29007.1,29120.85,28942.760000000002,6581.355199999969 -2017-10-28,29731.46,28624.7,30239.6,30164.250000000004,28942.760000000002,6581.355199999969 -2017-10-29,29731.46,28624.7,30239.6,30164.250000000004,28942.760000000002,6581.355199999969 -2017-10-30,-30747.350000000002,28624.7,30239.6,30164.250000000004,-29960.770000000004,4358.005199999963 -2017-10-31,-30747.350000000002,-30947.84,30577.9,30843.2,-29960.770000000004,2147.445199999962 -2017-11-01,30888.0,-30947.84,30610.2,-31041.539999999997,30235.399999999998,1782.5551999999552 -2017-11-02,30888.0,-30903.04,-31132.079999999998,31100.350000000002,30235.399999999998,1234.9851999999555 -2017-11-03,31093.92,30013.199999999997,-31132.079999999998,31100.350000000002,30246.13,2346.585199999954 -2017-11-04,31201.559999999998,30013.199999999997,-31132.079999999998,31912.5,30449.71,2346.585199999954 -2017-11-05,31201.559999999998,30013.199999999997,-31132.079999999998,31912.5,30449.71,2346.585199999954 -2017-11-06,31201.559999999998,30013.199999999997,31349.579999999998,31912.5,-31280.4,1303.9051999999538 -2017-11-07,-31342.399999999998,30257.820000000003,31349.579999999998,32236.25,30519.31,1572.7351999999555 -2017-11-08,-31342.399999999998,30325.590000000004,31363.5,32339.850000000002,30519.31,1572.7351999999555 -2017-11-09,-31342.399999999998,30325.590000000004,-31377.500000000004,32339.850000000002,-31431.600000000002,345.71519999995144 -2017-11-10,-31024.0,-30384.449999999997,-31377.500000000004,-31440.6,-31431.600000000002,1219.06519999995 -2017-11-11,-30723.2,-30384.449999999997,-31230.5,-31440.6,-31324.500000000004,1219.06519999995 -2017-11-12,-30723.2,-30384.449999999997,-31230.5,-31440.6,-31324.500000000004,1219.06519999995 -2017-11-13,31212.800000000003,30487.590000000004,31105.980000000003,-31440.6,-31324.500000000004,1397.8351999999468 -2017-11-14,31212.800000000003,30487.590000000004,-31340.32,-31314.6,31249.200000000004,1041.6951999999474 -2017-11-15,-31123.440000000002,-30420.63,-31340.32,-30841.2,-31092.300000000003,657.4551999999458 -2017-11-16,30890.579999999998,30706.829999999998,30889.48,30969.1,30405.63,3035.225199999946 -2017-11-17,-31298.399999999998,-30506.760000000002,-31325.0,-31307.600000000002,-31076.700000000004,179.08519999994314 -2017-11-18,-31298.399999999998,-30506.760000000002,-31325.0,-31307.600000000002,-31076.700000000004,179.08519999994314 -2017-11-19,-31298.399999999998,-30506.760000000002,-31325.0,-31307.600000000002,-31076.700000000004,179.08519999994314 -2017-11-20,31056.0,-30506.760000000002,-31325.0,-31307.600000000002,-31076.700000000004,567.2851999999439 -2017-11-21,31056.0,30766.23,31098.06,30992.059999999998,30458.699999999997,3210.72519999995 -2017-11-22,31396.8,30766.23,-31471.38,30992.059999999998,30458.699999999997,2668.1151999999456 -2017-11-23,31411.199999999997,31216.320000000003,-31471.38,31317.84,30505.68,2668.1151999999456 -2017-11-24,-31711.5,31216.320000000003,31620.94,31317.84,30505.68,2459.3951999999445 -2017-11-25,-31711.5,32022.0,31620.94,31319.63,30639.079999999998,2459.3951999999445 -2017-11-26,-31711.5,32022.0,31620.94,31319.63,30639.079999999998,2459.3951999999445 -2017-11-27,-31711.5,32022.0,31620.94,-31858.47,30639.079999999998,1763.0351999999402 -2017-11-28,31868.800000000003,-31033.6,-31923.499999999996,-31858.47,-31898.699999999997,1926.865199999942 -2017-11-29,-31985.5,-31033.6,-31923.499999999996,-31671.809999999998,-31898.699999999997,45.36519999994198 -2017-11-30,-31985.5,30595.5,30829.32,30933.0,-31121.4,738.0951999999415 -2017-12-01,-31888.600000000002,-30221.1,-31167.8,-31131.100000000002,-31085.100000000002,-304.40480000005846 -2017-12-02,-31759.399999999998,-30221.1,-31167.8,-31131.100000000002,-30752.1,-304.40480000005846 -2017-12-03,-31759.399999999998,-30221.1,-31167.8,-31131.100000000002,-30752.1,-304.40480000005846 -2017-12-04,-31759.399999999998,-30221.1,-31167.8,-31131.100000000002,-30752.1,-304.40480000005846 -2017-12-05,30394.65,29680.82,30418.08,-30903.600000000002,29568.4,1981.9051999999392 -2017-12-06,30394.65,29680.82,30418.08,-30874.479999999996,29568.4,1981.9051999999392 -2017-12-07,-30743.199999999997,29961.1,30986.56,30840.264,29948.88,1796.7051999999385 -2017-12-08,30920.559999999998,30154.54,-30967.0,-30994.71,30292.53,2541.4151999999376 -2017-12-09,30920.559999999998,30212.0,-30967.0,-30994.71,30432.020000000004,2541.4151999999376 -2017-12-10,30920.559999999998,30212.0,-30967.0,-30994.71,30432.020000000004,2541.4151999999376 -2017-12-11,-31098.74,30212.0,31152.96,31080.6,30432.020000000004,2321.7251999999207 -2017-12-12,-31098.74,-30292.079999999998,-31144.960000000003,-31249.399999999998,-30414.329999999998,1624.4051999999174 -2017-12-13,30996.9,-30292.079999999998,31024.2,31008.600000000002,30490.31,2701.265199999918 -2017-12-14,30996.9,30530.76,31024.2,-31171.82,30490.31,2529.045199999913 -2017-12-15,31277.4,30530.76,31039.859999999997,31296.600000000002,30666.63,2702.915199999912 -2017-12-16,31369.8,30657.640000000003,31351.32,31296.600000000002,31088.0,2702.915199999912 -2017-12-17,31369.8,30657.640000000003,31351.32,31296.600000000002,31088.0,2702.915199999912 -2017-12-18,31369.8,30657.640000000003,31351.32,31296.600000000002,31088.0,2702.915199999912 -2017-12-19,-31793.4,-30871.880000000005,-31773.269999999997,-31940.82,-31313.62,705.6651999999158 -2017-12-20,31532.94,-30871.880000000005,-31773.269999999997,-31940.82,-31313.62,1272.1251999999113 -2017-12-21,-31499.54,-30618.119999999995,-31486.53,31501.8,-31133.239999999998,1797.15519999991 -2017-12-22,31530.04,-30543.76,-31408.649999999998,31501.8,-31054.649999999998,1987.0951999999088 -2017-12-23,31530.04,-30377.359999999997,-31364.399999999998,31501.8,-30996.94,1987.0951999999088 -2017-12-24,31530.04,-30377.359999999997,-31364.399999999998,31501.8,-30996.94,1987.0951999999088 -2017-12-25,31530.04,-30377.359999999997,-31364.399999999998,31501.8,-30996.94,1987.0951999999088 -2017-12-26,-31543.68,30595.76,-31364.399999999998,-31384.879999999997,-30996.94,929.2951999999132 -2017-12-27,-31543.68,30595.76,31261.120000000003,31219.8,-30909.649999999998,1277.5151999999107 -2017-12-28,31219.02,30738.76,31261.120000000003,31219.8,-30745.800000000003,2433.7751999999127 -2017-12-29,-31481.440000000002,-30406.22,-31409.88,-31476.78,-30622.550000000003,1189.2451999999066 diff --git a/alphapy/examples/Trading System/systems/faang_closer_returns_1d.csv b/alphapy/examples/Trading System/systems/faang_closer_returns_1d.csv deleted file mode 100644 index 6f03aea..0000000 --- a/alphapy/examples/Trading System/systems/faang_closer_returns_1d.csv +++ /dev/null @@ -1,499 +0,0 @@ -date,return -2016-08-19,2.220446049250313e-16 -2016-08-20,0.0 -2016-08-21,0.0 -2016-08-22,-0.0019766999999999424 -2016-08-23,0.0030824931642376985 -2016-08-24,-0.006606534793687335 -2016-08-25,0.004267933813842717 -2016-08-26,0.004104912006775097 -2016-08-27,0.0 -2016-08-28,0.0 -2016-08-29,0.0029717898347676464 -2016-08-30,-0.004242947625705096 -2016-08-31,0.0005226936927096304 -2016-09-01,0.0018824107511470345 -2016-09-02,0.004241137328821587 -2016-09-03,0.0 -2016-09-04,0.0 -2016-09-05,0.0 -2016-09-06,0.017502300859255193 -2016-09-07,0.00027440490446850596 -2016-09-08,-0.006769168819236615 -2016-09-09,-0.025412981679501456 -2016-09-10,0.0 -2016-09-11,0.0 -2016-09-12,0.018015063058056446 -2016-09-13,-0.008430107560909783 -2016-09-14,0.010426457282463053 -2016-09-15,0.013480305789569602 -2016-09-16,0.005639420860773292 -2016-09-17,0.0 -2016-09-18,0.0 -2016-09-19,-0.007263289147146024 -2016-09-20,0.0027255893712647694 -2016-09-21,-0.0011455878183785906 -2016-09-22,0.01051682068915194 -2016-09-23,-0.006272527618697787 -2016-09-24,0.0 -2016-09-25,0.0 -2016-09-26,-0.008229443196589137 -2016-09-27,0.014031604974783951 -2016-09-28,0.00604186773996207 -2016-09-29,-0.00814507886665472 -2016-09-30,0.008083413879760304 -2016-10-01,0.0 -2016-10-02,0.0 -2016-10-03,0.00699909709173796 -2016-10-04,-0.0006433627237524275 -2016-10-05,0.010194007701313401 -2016-10-06,-0.0005249523548678248 -2016-10-07,-0.000900417946041121 -2016-10-08,0.0 -2016-10-09,0.0 -2016-10-10,0.006416560860518228 -2016-10-11,-0.01044999782276057 -2016-10-12,0.0012879890868506827 -2016-10-13,-0.004099858636995468 -2016-10-14,0.002309351752352029 -2016-10-15,0.0 -2016-10-16,0.0 -2016-10-17,-0.005813783023897678 -2016-10-18,0.04448440251392238 -2016-10-19,0.008000097224514224 -2016-10-20,-0.000854750209245636 -2016-10-21,0.011750825450213886 -2016-10-22,0.0 -2016-10-23,0.0 -2016-10-24,0.010394856959407894 -2016-10-25,-0.00405634110825337 -2016-10-26,-0.01012538950575026 -2016-10-27,-0.006941592388168161 -2016-10-28,-0.008080147491633216 -2016-10-29,0.0 -2016-10-30,0.0 -2016-10-31,-0.0025707081840423696 -2016-11-01,-0.010626095650896006 -2016-11-02,-0.014209407893487791 -2016-11-03,-0.015976671629929773 -2016-11-04,-0.004181342482462491 -2016-11-05,0.0 -2016-11-06,0.0 -2016-11-07,0.02265819983831796 -2016-11-08,0.0072398313065400455 -2016-11-09,-0.010798396486940032 -2016-11-10,-0.03408446057934533 -2016-11-11,-0.005929192772749614 -2016-11-12,0.0 -2016-11-13,0.0 -2016-11-14,-0.0240985106979279 -2016-11-15,0.01905860710964924 -2016-11-16,0.008596798277447748 -2016-11-17,0.00614561990983109 -2016-11-18,-0.0023097805242427993 -2016-11-19,0.0 -2016-11-20,0.0 -2016-11-21,0.02319419921662491 -2016-11-22,0.0011481595727791394 -2016-11-23,-0.005409191349930231 -2016-11-24,0.0 -2016-11-25,0.00015408152699092348 -2016-11-26,0.0 -2016-11-27,0.0 -2016-11-28,-0.003155940198070528 -2016-11-29,0.0013643696774972014 -2016-11-30,-0.013061636195479731 -2016-12-01,-0.01187528617906941 -2016-12-02,0.0065905686375544725 -2016-12-03,0.0 -2016-12-04,0.0 -2016-12-05,0.00773607590649239 -2016-12-06,0.01121016269437769 -2016-12-07,0.009588193213542429 -2016-12-08,0.0003113223858890457 -2016-12-09,0.007822879165423124 -2016-12-10,0.0 -2016-12-11,0.0 -2016-12-12,-0.006951507759196618 -2016-12-13,0.014672817074697697 -2016-12-14,-0.0014977709709621534 -2016-12-15,0.0017010287997036855 -2016-12-16,-0.004364941158150004 -2016-12-17,0.0 -2016-12-18,0.0 -2016-12-19,0.0048083429526728505 -2016-12-20,0.0017250129676571468 -2016-12-21,0.0014187053985241782 -2016-12-22,-0.007143873249467747 -2016-12-23,-0.0017126973378087662 -2016-12-24,0.0 -2016-12-25,0.0 -2016-12-26,0.0 -2016-12-27,0.010158164531348213 -2016-12-28,-0.007661331559934648 -2016-12-29,-0.004101379717379117 -2016-12-30,-0.012695965520566843 -2016-12-31,0.0 -2017-01-01,0.0 -2017-01-02,0.0 -2017-01-03,0.014570289130705572 -2017-01-04,0.006632061265640532 -2017-01-05,0.015094060588947578 -2017-01-06,0.012337861552315976 -2017-01-07,0.0 -2017-01-08,0.0 -2017-01-09,0.004729199325819167 -2017-01-10,-0.0027987815932913884 -2017-01-11,0.0064499550046261955 -2017-01-12,0.0014723792068025432 -2017-01-13,0.010504455064903606 -2017-01-14,0.0 -2017-01-15,0.0 -2017-01-16,0.0 -2017-01-17,-0.0029214962337680284 -2017-01-18,0.000455135365362791 -2017-01-19,0.006033809453054095 -2017-01-20,0.0005905145644624632 -2017-01-21,0.0 -2017-01-22,0.0 -2017-01-23,0.007440266801403306 -2017-01-24,0.006700134859505491 -2017-01-25,0.01094978860139273 -2017-01-26,0.0015376517661256361 -2017-01-27,0.0004765379351068244 -2017-01-28,0.0 -2017-01-29,0.0 -2017-01-30,-0.010231947853548973 -2017-01-31,-0.004672318850449497 -2017-02-01,0.017826010856724528 -2017-02-02,-0.0035787935555101624 -2017-02-03,-0.0038864923990367117 -2017-02-04,0.0 -2017-02-05,0.0 -2017-02-06,0.004295394130674479 -2017-02-07,0.008889513220064149 -2017-02-08,0.007246373099900616 -2017-02-09,8.32292050092498e-05 -2017-02-10,0.003142530864066684 -2017-02-11,0.0 -2017-02-12,0.0 -2017-02-13,0.0024530266019398006 -2017-02-14,-0.0008060953449404717 -2017-02-15,0.0029909894986428487 -2017-02-16,0.001464504056134741 -2017-02-17,0.0016332393706037784 -2017-02-18,0.0 -2017-02-19,0.0 -2017-02-20,0.0 -2017-02-21,0.005560583152849796 -2017-02-22,0.00620803697560679 -2017-02-23,-0.004333106865295755 -2017-02-24,-0.0013927650776368594 -2017-02-25,0.0 -2017-02-26,0.0 -2017-02-27,0.003274400403654232 -2017-02-28,-0.004906866548296751 -2017-03-01,0.012242477034922095 -2017-03-02,-0.008940111256843464 -2017-03-03,0.0012761636215157246 -2017-03-04,0.0 -2017-03-05,0.0 -2017-03-06,0.0025815650148952596 -2017-03-07,0.00013170065411061138 -2017-03-08,-7.856247900761559e-05 -2017-03-09,0.0021460513326794217 -2017-03-10,0.00266371148983513 -2017-03-11,0.0 -2017-03-12,0.0 -2017-03-13,0.006189384611527071 -2017-03-14,-0.0013296145658867609 -2017-03-15,0.006231816583440075 -2017-03-16,6.770687816182175e-06 -2017-03-17,7.961622369823118e-05 -2017-03-18,0.0 -2017-03-19,0.0 -2017-03-20,0.0032733855669888623 -2017-03-21,-0.01615059941471153 -2017-03-22,0.0052774652300784375 -2017-03-23,-0.004406118866977726 -2017-03-24,-0.00043165490197905054 -2017-03-25,0.0 -2017-03-26,0.0 -2017-03-27,0.00423647226951851 -2017-03-28,0.01029891869167776 -2017-03-29,0.00976642372254477 -2017-03-30,0.0019544849547954435 -2017-03-31,0.0006864087920837036 -2017-04-01,0.0 -2017-04-02,0.0 -2017-04-03,0.0023810008212761247 -2017-04-04,0.0012722685095452047 -2017-04-05,-0.003702669538766745 -2017-04-06,-0.004662365612843056 -2017-04-07,-0.0033259387225614834 -2017-04-08,0.0 -2017-04-09,0.0 -2017-04-10,0.0037318690020351486 -2017-04-11,-0.004481945377835417 -2017-04-12,-0.0019392864766002438 -2017-04-13,-0.005430213377537685 -2017-04-14,0.0 -2017-04-15,0.0 -2017-04-16,0.0 -2017-04-17,0.01744615554361406 -2017-04-18,-0.006564978198540183 -2017-04-19,-0.004320618797610587 -2017-04-20,0.008098977925211193 -2017-04-21,0.0009434080060386485 -2017-04-22,0.0 -2017-04-23,0.0 -2017-04-24,0.012172826613235488 -2017-04-25,0.016393078946025375 -2017-04-26,-0.003400720336725782 -2017-04-27,0.00811064598967004 -2017-04-28,0.010567191070443904 -2017-04-29,0.0 -2017-04-30,0.0 -2017-05-01,0.017685664736846185 -2017-05-02,0.003569729706044855 -2017-05-03,-0.0016925099113830333 -2017-05-04,0.0007750651106837037 -2017-05-05,4.8932646528943025e-05 -2017-05-06,0.0 -2017-05-07,0.0 -2017-05-08,0.01094648272689458 -2017-05-09,0.0022373251704863595 -2017-05-10,0.0012437526673907406 -2017-05-11,-0.0016984802434362178 -2017-05-12,0.008742084707739517 -2017-05-13,0.0 -2017-05-14,0.0 -2017-05-15,-0.0014980727856830356 -2017-05-16,0.0011920840396626442 -2017-05-17,-0.029944950213551924 -2017-05-18,0.01485395807804446 -2017-05-19,0.0038550235720158366 -2017-05-20,0.0 -2017-05-21,0.0 -2017-05-22,0.005836265695866594 -2017-05-23,0.0019903404365635424 -2017-05-24,0.0049841169668429774 -2017-05-25,0.015315473054917872 -2017-05-26,-0.0001099981903551761 -2017-05-27,0.0 -2017-05-28,0.0 -2017-05-29,0.0 -2017-05-30,0.002125119436470868 -2017-05-31,-0.00474233744691277 -2017-06-01,0.001044203291957091 -2017-06-02,0.012015779183749853 -2017-06-03,0.0 -2017-06-04,0.0 -2017-06-05,0.0003392318417756446 -2017-06-06,-0.0032818242720080715 -2017-06-07,0.0044703388446067915 -2017-06-08,0.00239181338976735 -2017-06-09,-0.03647602336042388 -2017-06-10,0.0 -2017-06-11,0.0 -2017-06-12,-0.019600670756011573 -2017-06-13,0.011719996633973828 -2017-06-14,-0.004517691146136205 -2017-06-15,-0.006462744925185726 -2017-06-16,0.003649830095089568 -2017-06-17,0.0 -2017-06-18,0.0 -2017-06-19,0.01474666392254731 -2017-06-20,-0.006070728522620472 -2017-06-21,0.011208703959754596 -2017-06-22,-0.0017299594296935616 -2017-06-23,0.009586664357720265 -2017-06-24,0.0 -2017-06-25,0.0 -2017-06-26,-0.00791035489361247 -2017-06-27,-0.023316985476341978 -2017-06-28,0.015058079881250297 -2017-06-29,-0.017436066678588102 -2017-06-30,-0.003885212744659694 -2017-07-01,0.0 -2017-07-02,0.0 -2017-07-03,-0.013723681554019707 -2017-07-04,0.0 -2017-07-05,0.011924345490909483 -2017-07-06,-0.007856270148961508 -2017-07-07,0.01665639487976467 -2017-07-08,0.0 -2017-07-09,0.0 -2017-07-10,0.01270344209761931 -2017-07-11,0.005095059453382955 -2017-07-12,0.015823497855613766 -2017-07-13,0.0013264449122236677 -2017-07-14,0.008285122367519993 -2017-07-15,0.0 -2017-07-16,0.0 -2017-07-17,0.002520250238294164 -2017-07-18,0.03646227724287132 -2017-07-19,0.004568602880205885 -2017-07-20,-0.00045973516866448794 -2017-07-21,0.0049118814817794565 -2017-07-22,0.0 -2017-07-23,0.0 -2017-07-24,0.006982952864066805 -2017-07-25,-0.006477181272521837 -2017-07-26,0.005253415236657233 -2017-07-27,-0.008517760122170537 -2017-07-28,-0.0011895227266641717 -2017-07-29,0.0 -2017-07-30,0.0 -2017-07-31,-0.016076010474634805 -2017-08-01,0.004605653993462244 -2017-08-02,0.007429078037390235 -2017-08-03,-0.007736774885445219 -2017-08-04,0.004711013210118242 -2017-08-05,0.0 -2017-08-06,0.0 -2017-08-07,0.003923088780792616 -2017-08-08,0.000648545075997653 -2017-08-09,-0.004093525962214795 -2017-08-10,-0.027789514245592062 -2017-08-11,0.010063720711702473 -2017-08-12,0.0 -2017-08-13,0.0 -2017-08-14,0.010553782348560858 -2017-08-15,-0.0006964212280783366 -2017-08-16,0.00014039931479548073 -2017-08-17,-0.01889010458067142 -2017-08-18,-6.087848366631565e-05 -2017-08-19,0.0 -2017-08-20,0.0 -2017-08-21,-0.0018554297739434844 -2017-08-22,0.015533317270306934 -2017-08-23,-0.002497102934827855 -2017-08-24,-0.005445441760645209 -2017-08-25,-0.006359319759217663 -2017-08-26,0.0 -2017-08-27,0.0 -2017-08-28,0.004184983139484011 -2017-08-29,0.008022154024211137 -2017-08-30,0.014077349305761988 -2017-08-31,0.00824387901325907 -2017-09-01,-0.0010133676620260879 -2017-09-02,0.0 -2017-09-03,0.0 -2017-09-04,0.0 -2017-09-05,-0.008963923599061396 -2017-09-06,0.007488780562387554 -2017-09-07,0.0042289046498253935 -2017-09-08,-0.013173317580561061 -2017-09-09,0.0 -2017-09-10,0.0 -2017-09-11,0.015416394784739662 -2017-09-12,0.0038739180064544243 -2017-09-13,0.0012254744196069645 -2017-09-14,-0.008815268141659782 -2017-09-15,3.8948642555380886e-05 -2017-09-16,0.0 -2017-09-17,0.0 -2017-09-18,-0.004283069052282373 -2017-09-19,0.004789691135404839 -2017-09-20,-0.001012202899489667 -2017-09-21,-0.0028820721558969753 -2017-09-22,-0.006936574508851656 -2017-09-23,0.0 -2017-09-24,0.0 -2017-09-25,-0.025138000071466382 -2017-09-26,0.006392085530634972 -2017-09-27,0.015682140033445258 -2017-09-28,0.0007953566555651825 -2017-09-29,0.0071110130077043365 -2017-09-30,0.0 -2017-10-01,0.0 -2017-10-02,-0.008430711859048534 -2017-10-03,0.004455771989419155 -2017-10-04,0.003309684397314827 -2017-10-05,0.023663578955761322 -2017-10-06,0.008274409857515375 -2017-10-07,0.0 -2017-10-08,0.0 -2017-10-09,-0.00021135549864259584 -2017-10-10,-0.0043992309324013545 -2017-10-11,0.007123303261804992 -2017-10-12,0.001166357726604783 -2017-10-13,0.007154400232885383 -2017-10-14,0.0 -2017-10-15,0.0 -2017-10-16,0.008712838950892143 -2017-10-17,0.0001998199618145069 -2017-10-18,-0.00692626342960867 -2017-10-19,-0.011045481081886965 -2017-10-20,-0.00039198100579318496 -2017-10-21,0.0 -2017-10-22,0.0 -2017-10-23,-0.013165531062315772 -2017-10-24,0.008061850730660902 -2017-10-25,-0.004551487053533809 -2017-10-26,0.002707193208362213 -2017-10-27,0.054701937184086 -2017-10-28,0.0 -2017-10-29,0.0 -2017-10-30,0.007033776148992921 -2017-10-31,8.605302226016853e-05 -2017-11-01,0.00334683343811637 -2017-11-02,-0.0029320203998577377 -2017-11-03,0.010357383798537567 -2017-11-04,0.0 -2017-11-05,0.0 -2017-11-06,0.0037731852107398822 -2017-11-07,-0.0011725472204969778 -2017-11-08,0.0 -2017-11-09,-0.0016528286856620777 -2017-11-10,-0.005603005658312399 -2017-11-11,0.0 -2017-11-12,0.0 -2017-11-13,0.0027666555440064577 -2017-11-14,-0.001748654528646032 -2017-11-15,-0.00915940460366782 -2017-11-16,0.011928092308623572 -2017-11-17,-0.007669370269785314 -2017-11-18,0.0 -2017-11-19,0.0 -2017-11-20,-0.00041279810230709924 -2017-11-21,0.014735659451300842 -2017-11-22,0.004229667602330345 -2017-11-23,0.0 -2017-11-24,0.00745321906196561 -2017-11-25,0.0 -2017-11-26,0.0 -2017-11-27,0.0030477701872400065 -2017-11-28,0.00039797953887465987 -2017-11-29,-0.03315426280662692 -2017-11-30,0.006849587471798202 -2017-12-01,-0.008602058756149389 -2017-12-02,0.0 -2017-12-03,0.0 -2017-12-04,-0.01603235682020343 -2017-12-05,0.00441310767542924 -2017-12-06,0.00846484496647859 -2017-12-07,0.008553857381969898 -2017-12-08,0.003445120079279551 -2017-12-09,0.0 -2017-12-10,0.0 -2017-12-11,0.0031099963914891227 -2017-12-12,-0.005180287892549984 -2017-12-13,0.004793630437407437 -2017-12-14,0.00464480954910762 -2017-12-15,0.007955703343966114 -2017-12-16,0.0 -2017-12-17,0.0 -2017-12-18,0.008184138854549161 -2017-12-19,-0.008540953901812864 -2017-12-20,-0.0028387235954262513 -2017-12-21,-0.0009082045171374675 -2017-12-22,-0.00030366484842214714 -2017-12-23,0.0 -2017-12-24,0.0 -2017-12-25,0.0 -2017-12-26,-0.007886401177494395 -2017-12-27,0.00011385599674484936 -2017-12-28,0.007692965452065881 -2017-12-29,-0.007755641308840877 diff --git a/alphapy/examples/Trading System/systems/faang_closer_trades_1d.csv b/alphapy/examples/Trading System/systems/faang_closer_trades_1d.csv deleted file mode 100644 index d96ca00..0000000 --- a/alphapy/examples/Trading System/systems/faang_closer_trades_1d.csv +++ /dev/null @@ -1,1748 +0,0 @@ -date,name,order,quantity,price -2016-08-19,nflx,se,-1,95.87 -2016-08-19,amzn,se,-1,757.31 -2016-08-19,fb,se,-1,123.56 -2016-08-19,aapl,le,1,109.36 -2016-08-19,googl,se,-1,799.65 -2016-08-22,amzn,sx,1,759.48 -2016-08-22,amzn,le,1,759.48 -2016-08-22,fb,sx,1,124.15 -2016-08-22,fb,le,1,124.15 -2016-08-22,aapl,lx,-1,108.51 -2016-08-22,aapl,se,-1,108.51 -2016-08-23,nflx,sx,1,95.94 -2016-08-23,nflx,le,1,95.94 -2016-08-23,aapl,sx,1,108.85 -2016-08-23,aapl,le,1,108.85 -2016-08-24,nflx,lx,-1,95.18 -2016-08-24,nflx,se,-1,95.18 -2016-08-24,amzn,lx,-1,757.25 -2016-08-24,amzn,se,-1,757.25 -2016-08-24,fb,lx,-1,123.48 -2016-08-24,fb,se,-1,123.48 -2016-08-24,aapl,lx,-1,108.03 -2016-08-24,aapl,se,-1,108.03 -2016-08-25,nflx,sx,1,97.32 -2016-08-25,nflx,le,1,97.32 -2016-08-25,amzn,sx,1,759.22 -2016-08-25,amzn,le,1,759.22 -2016-08-25,fb,sx,1,123.89 -2016-08-25,fb,le,1,123.89 -2016-08-26,googl,sx,1,793.22 -2016-08-26,googl,le,1,793.22 -2016-08-29,nflx,lx,-1,97.3 -2016-08-29,nflx,se,-1,97.3 -2016-08-30,nflx,sx,1,97.45 -2016-08-30,nflx,le,1,97.45 -2016-08-30,amzn,lx,-1,767.58 -2016-08-30,amzn,se,-1,767.58 -2016-08-30,fb,lx,-1,125.84 -2016-08-30,fb,se,-1,125.84 -2016-08-30,googl,lx,-1,791.92 -2016-08-30,googl,se,-1,791.92 -2016-08-31,amzn,sx,1,769.16 -2016-08-31,amzn,le,1,769.16 -2016-08-31,fb,sx,1,126.12 -2016-08-31,fb,le,1,126.12 -2016-08-31,aapl,sx,1,106.1 -2016-08-31,aapl,le,1,106.1 -2016-09-01,nflx,lx,-1,97.38 -2016-09-01,nflx,se,-1,97.38 -2016-09-01,googl,sx,1,791.4 -2016-09-01,googl,le,1,791.4 -2016-09-06,nflx,sx,1,100.09 -2016-09-06,nflx,le,1,100.09 -2016-09-06,aapl,lx,-1,107.7 -2016-09-06,aapl,se,-1,107.7 -2016-09-07,nflx,lx,-1,99.15 -2016-09-07,nflx,se,-1,99.15 -2016-09-07,amzn,lx,-1,784.48 -2016-09-07,amzn,se,-1,784.48 -2016-09-07,aapl,sx,1,108.36 -2016-09-07,aapl,le,1,108.36 -2016-09-07,googl,lx,-1,807.99 -2016-09-07,googl,se,-1,807.99 -2016-09-08,nflx,sx,1,99.66 -2016-09-08,nflx,le,1,99.66 -2016-09-08,fb,lx,-1,130.27 -2016-09-08,fb,se,-1,130.27 -2016-09-08,aapl,lx,-1,105.52 -2016-09-08,aapl,se,-1,105.52 -2016-09-09,nflx,lx,-1,96.5 -2016-09-09,nflx,se,-1,96.5 -2016-09-12,nflx,sx,1,99.05 -2016-09-12,nflx,le,1,99.05 -2016-09-12,amzn,sx,1,771.49 -2016-09-12,amzn,le,1,771.49 -2016-09-12,fb,sx,1,128.69 -2016-09-12,fb,le,1,128.69 -2016-09-12,aapl,sx,1,105.44 -2016-09-12,aapl,le,1,105.44 -2016-09-12,googl,sx,1,798.82 -2016-09-12,googl,le,1,798.82 -2016-09-13,nflx,lx,-1,96.09 -2016-09-13,nflx,se,-1,96.09 -2016-09-13,amzn,lx,-1,761.01 -2016-09-13,amzn,se,-1,761.01 -2016-09-13,fb,lx,-1,127.21 -2016-09-13,fb,se,-1,127.21 -2016-09-13,googl,lx,-1,788.72 -2016-09-13,googl,se,-1,788.72 -2016-09-14,nflx,sx,1,97.01 -2016-09-14,nflx,le,1,97.01 -2016-09-14,amzn,sx,1,761.09 -2016-09-14,amzn,le,1,761.09 -2016-09-14,fb,sx,1,127.77 -2016-09-14,fb,le,1,127.77 -2016-09-14,googl,sx,1,790.46 -2016-09-14,googl,le,1,790.46 -2016-09-16,aapl,lx,-1,114.92 -2016-09-16,aapl,se,-1,114.92 -2016-09-16,googl,lx,-1,797.97 -2016-09-16,googl,se,-1,797.97 -2016-09-19,nflx,lx,-1,98.06 -2016-09-19,nflx,se,-1,98.06 -2016-09-19,amzn,lx,-1,775.1 -2016-09-19,amzn,se,-1,775.1 -2016-09-19,fb,lx,-1,128.65 -2016-09-19,fb,se,-1,128.65 -2016-09-20,nflx,sx,1,98.25 -2016-09-20,nflx,le,1,98.25 -2016-09-20,amzn,sx,1,780.22 -2016-09-20,amzn,le,1,780.22 -2016-09-20,googl,sx,1,799.78 -2016-09-20,googl,le,1,799.78 -2016-09-21,nflx,lx,-1,94.88 -2016-09-21,nflx,se,-1,94.88 -2016-09-21,fb,sx,1,129.94 -2016-09-21,fb,le,1,129.94 -2016-09-22,nflx,sx,1,95.83 -2016-09-22,nflx,le,1,95.83 -2016-09-22,aapl,sx,1,114.62 -2016-09-22,aapl,le,1,114.62 -2016-09-23,fb,lx,-1,127.96 -2016-09-23,fb,se,-1,127.96 -2016-09-23,aapl,lx,-1,112.71 -2016-09-23,aapl,se,-1,112.71 -2016-09-23,googl,lx,-1,814.96 -2016-09-23,googl,se,-1,814.96 -2016-09-26,nflx,lx,-1,94.56 -2016-09-26,nflx,se,-1,94.56 -2016-09-26,amzn,lx,-1,799.16 -2016-09-26,amzn,se,-1,799.16 -2016-09-26,aapl,sx,1,112.88 -2016-09-26,aapl,le,1,112.88 -2016-09-27,nflx,sx,1,97.07 -2016-09-27,nflx,le,1,97.07 -2016-09-27,amzn,sx,1,816.11 -2016-09-27,amzn,le,1,816.11 -2016-09-27,fb,sx,1,128.69 -2016-09-27,fb,le,1,128.69 -2016-09-27,googl,sx,1,810.73 -2016-09-27,googl,le,1,810.73 -2016-09-28,googl,lx,-1,810.06 -2016-09-28,googl,se,-1,810.06 -2016-09-29,nflx,lx,-1,96.67 -2016-09-29,nflx,se,-1,96.67 -2016-09-29,fb,lx,-1,128.09 -2016-09-29,fb,se,-1,128.09 -2016-09-29,aapl,lx,-1,112.18 -2016-09-29,aapl,se,-1,112.18 -2016-09-30,nflx,sx,1,98.55 -2016-09-30,nflx,le,1,98.55 -2016-09-30,fb,sx,1,128.27 -2016-09-30,fb,le,1,128.27 -2016-09-30,aapl,sx,1,113.05 -2016-09-30,aapl,le,1,113.05 -2016-09-30,googl,sx,1,804.06 -2016-09-30,googl,le,1,804.06 -2016-10-03,amzn,lx,-1,836.74 -2016-10-03,amzn,se,-1,836.74 -2016-10-03,aapl,lx,-1,112.52 -2016-10-03,aapl,se,-1,112.52 -2016-10-03,googl,lx,-1,800.38 -2016-10-03,googl,se,-1,800.38 -2016-10-04,nflx,lx,-1,102.34 -2016-10-04,nflx,se,-1,102.34 -2016-10-04,fb,lx,-1,128.19 -2016-10-04,fb,se,-1,128.19 -2016-10-04,aapl,sx,1,113.0 -2016-10-04,aapl,le,1,113.0 -2016-10-04,googl,sx,1,802.79 -2016-10-04,googl,le,1,802.79 -2016-10-05,nflx,sx,1,106.28 -2016-10-05,nflx,le,1,106.28 -2016-10-05,amzn,sx,1,844.36 -2016-10-05,amzn,le,1,844.36 -2016-10-05,fb,sx,1,128.47 -2016-10-05,fb,le,1,128.47 -2016-10-05,googl,lx,-1,801.23 -2016-10-05,googl,se,-1,801.23 -2016-10-06,nflx,lx,-1,105.07 -2016-10-06,nflx,se,-1,105.07 -2016-10-06,amzn,lx,-1,841.66 -2016-10-06,amzn,se,-1,841.66 -2016-10-06,googl,sx,1,803.08 -2016-10-06,googl,le,1,803.08 -2016-10-07,googl,lx,-1,800.71 -2016-10-07,googl,se,-1,800.71 -2016-10-10,amzn,sx,1,841.71 -2016-10-10,amzn,le,1,841.71 -2016-10-10,googl,sx,1,814.17 -2016-10-10,googl,le,1,814.17 -2016-10-11,amzn,lx,-1,831.0 -2016-10-11,amzn,se,-1,831.0 -2016-10-11,fb,lx,-1,128.88 -2016-10-11,fb,se,-1,128.88 -2016-10-11,googl,lx,-1,809.57 -2016-10-11,googl,se,-1,809.57 -2016-10-12,amzn,sx,1,834.09 -2016-10-12,amzn,le,1,834.09 -2016-10-12,fb,sx,1,129.05 -2016-10-12,fb,le,1,129.05 -2016-10-12,googl,sx,1,811.77 -2016-10-12,googl,le,1,811.77 -2016-10-13,nflx,sx,1,100.23 -2016-10-13,nflx,le,1,100.23 -2016-10-13,amzn,lx,-1,829.28 -2016-10-13,amzn,se,-1,829.28 -2016-10-13,fb,lx,-1,127.82 -2016-10-13,fb,se,-1,127.82 -2016-10-13,aapl,lx,-1,116.98 -2016-10-13,aapl,se,-1,116.98 -2016-10-13,googl,lx,-1,804.08 -2016-10-13,googl,se,-1,804.08 -2016-10-14,fb,sx,1,127.88 -2016-10-14,fb,le,1,127.88 -2016-10-14,aapl,sx,1,117.63 -2016-10-14,aapl,le,1,117.63 -2016-10-14,googl,sx,1,804.6 -2016-10-14,googl,le,1,804.6 -2016-10-17,nflx,lx,-1,99.8 -2016-10-17,nflx,se,-1,99.8 -2016-10-17,fb,lx,-1,127.54 -2016-10-17,fb,se,-1,127.54 -2016-10-17,aapl,lx,-1,117.55 -2016-10-17,aapl,se,-1,117.55 -2016-10-18,nflx,sx,1,118.79 -2016-10-18,nflx,le,1,118.79 -2016-10-18,amzn,sx,1,817.65 -2016-10-18,amzn,le,1,817.65 -2016-10-18,fb,sx,1,128.57 -2016-10-18,fb,le,1,128.57 -2016-10-20,amzn,lx,-1,810.32 -2016-10-20,amzn,se,-1,810.32 -2016-10-20,fb,lx,-1,130.0 -2016-10-20,fb,se,-1,130.0 -2016-10-20,googl,lx,-1,821.63 -2016-10-20,googl,se,-1,821.63 -2016-10-21,amzn,sx,1,818.99 -2016-10-21,amzn,le,1,818.99 -2016-10-21,fb,sx,1,132.07 -2016-10-21,fb,le,1,132.07 -2016-10-21,googl,sx,1,824.06 -2016-10-21,googl,le,1,824.06 -2016-10-24,nflx,lx,-1,127.33 -2016-10-24,nflx,se,-1,127.33 -2016-10-24,aapl,sx,1,117.65 -2016-10-24,aapl,le,1,117.65 -2016-10-25,amzn,lx,-1,835.18 -2016-10-25,amzn,se,-1,835.18 -2016-10-25,fb,lx,-1,132.29 -2016-10-25,fb,se,-1,132.29 -2016-10-25,googl,lx,-1,828.55 -2016-10-25,googl,se,-1,828.55 -2016-10-26,nflx,sx,1,126.97 -2016-10-26,nflx,le,1,126.97 -2016-10-26,aapl,lx,-1,115.59 -2016-10-26,aapl,se,-1,115.59 -2016-10-27,nflx,lx,-1,126.47 -2016-10-27,nflx,se,-1,126.47 -2016-10-28,nflx,sx,1,126.57 -2016-10-28,nflx,le,1,126.57 -2016-10-28,fb,sx,1,131.29 -2016-10-28,fb,le,1,131.29 -2016-10-28,googl,sx,1,819.56 -2016-10-28,googl,le,1,819.56 -2016-10-31,nflx,lx,-1,124.87 -2016-10-31,nflx,se,-1,124.87 -2016-10-31,amzn,sx,1,789.82 -2016-10-31,amzn,le,1,789.82 -2016-10-31,fb,lx,-1,130.99 -2016-10-31,fb,se,-1,130.99 -2016-10-31,googl,lx,-1,809.9 -2016-10-31,googl,se,-1,809.9 -2016-11-01,amzn,lx,-1,785.41 -2016-11-01,amzn,se,-1,785.41 -2016-11-02,aapl,sx,1,111.59 -2016-11-02,aapl,le,1,111.59 -2016-11-03,amzn,sx,1,767.03 -2016-11-03,amzn,le,1,767.03 -2016-11-03,aapl,lx,-1,109.83 -2016-11-03,aapl,se,-1,109.83 -2016-11-04,amzn,lx,-1,755.05 -2016-11-04,amzn,se,-1,755.05 -2016-11-04,fb,sx,1,120.75 -2016-11-04,fb,le,1,120.75 -2016-11-07,nflx,sx,1,124.58 -2016-11-07,nflx,le,1,124.58 -2016-11-07,amzn,sx,1,784.93 -2016-11-07,amzn,le,1,784.93 -2016-11-07,aapl,sx,1,110.41 -2016-11-07,aapl,le,1,110.41 -2016-11-07,googl,sx,1,802.03 -2016-11-07,googl,le,1,802.03 -2016-11-08,nflx,lx,-1,124.34 -2016-11-08,nflx,se,-1,124.34 -2016-11-09,amzn,lx,-1,771.88 -2016-11-09,amzn,se,-1,771.88 -2016-11-09,fb,lx,-1,123.18 -2016-11-09,fb,se,-1,123.18 -2016-11-09,aapl,lx,-1,110.88 -2016-11-09,aapl,se,-1,110.88 -2016-11-09,googl,lx,-1,805.59 -2016-11-09,googl,se,-1,805.59 -2016-11-11,aapl,sx,1,108.43 -2016-11-11,aapl,le,1,108.43 -2016-11-14,aapl,lx,-1,105.71 -2016-11-14,aapl,se,-1,105.71 -2016-11-15,nflx,sx,1,113.59 -2016-11-15,nflx,le,1,113.59 -2016-11-15,amzn,sx,1,743.24 -2016-11-15,amzn,le,1,743.24 -2016-11-15,fb,sx,1,117.2 -2016-11-15,fb,le,1,117.2 -2016-11-15,aapl,sx,1,107.11 -2016-11-15,aapl,le,1,107.11 -2016-11-15,googl,sx,1,775.16 -2016-11-15,googl,le,1,775.16 -2016-11-16,fb,lx,-1,116.34 -2016-11-16,fb,se,-1,116.34 -2016-11-17,nflx,lx,-1,115.03 -2016-11-17,nflx,se,-1,115.03 -2016-11-17,fb,sx,1,117.79 -2016-11-17,fb,le,1,117.79 -2016-11-17,aapl,lx,-1,109.95 -2016-11-17,aapl,se,-1,109.95 -2016-11-18,nflx,sx,1,115.21 -2016-11-18,nflx,le,1,115.21 -2016-11-18,fb,lx,-1,117.02 -2016-11-18,fb,se,-1,117.02 -2016-11-18,aapl,sx,1,110.06 -2016-11-18,aapl,le,1,110.06 -2016-11-18,googl,lx,-1,775.97 -2016-11-18,googl,se,-1,775.97 -2016-11-21,fb,sx,1,121.77 -2016-11-21,fb,le,1,121.77 -2016-11-21,googl,sx,1,784.8 -2016-11-21,googl,le,1,784.8 -2016-11-22,fb,lx,-1,121.47 -2016-11-22,fb,se,-1,121.47 -2016-11-23,nflx,lx,-1,117.69 -2016-11-23,nflx,se,-1,117.69 -2016-11-23,amzn,lx,-1,780.12 -2016-11-23,amzn,se,-1,780.12 -2016-11-23,aapl,lx,-1,111.23 -2016-11-23,aapl,se,-1,111.23 -2016-11-23,googl,lx,-1,779.0 -2016-11-23,googl,se,-1,779.0 -2016-11-25,amzn,sx,1,780.37 -2016-11-25,amzn,le,1,780.37 -2016-11-25,aapl,sx,1,111.79 -2016-11-25,aapl,le,1,111.79 -2016-11-25,googl,sx,1,780.23 -2016-11-25,googl,le,1,780.23 -2016-11-28,amzn,lx,-1,766.77 -2016-11-28,amzn,se,-1,766.77 -2016-11-28,fb,sx,1,120.41 -2016-11-28,fb,le,1,120.41 -2016-11-28,aapl,lx,-1,111.57 -2016-11-28,aapl,se,-1,111.57 -2016-11-29,nflx,sx,1,117.51 -2016-11-29,nflx,le,1,117.51 -2016-11-30,nflx,lx,-1,117.0 -2016-11-30,nflx,se,-1,117.0 -2016-11-30,fb,lx,-1,118.42 -2016-11-30,fb,se,-1,118.42 -2016-11-30,googl,lx,-1,775.88 -2016-11-30,googl,se,-1,775.88 -2016-12-01,nflx,sx,1,117.22 -2016-12-01,nflx,le,1,117.22 -2016-12-02,fb,sx,1,115.4 -2016-12-02,fb,le,1,115.4 -2016-12-02,aapl,sx,1,109.9 -2016-12-02,aapl,le,1,109.9 -2016-12-02,googl,sx,1,764.46 -2016-12-02,googl,le,1,764.46 -2016-12-05,nflx,lx,-1,119.16 -2016-12-05,nflx,se,-1,119.16 -2016-12-05,amzn,sx,1,759.36 -2016-12-05,amzn,le,1,759.36 -2016-12-05,aapl,lx,-1,109.11 -2016-12-05,aapl,se,-1,109.11 -2016-12-06,nflx,sx,1,124.57 -2016-12-06,nflx,le,1,124.57 -2016-12-06,fb,lx,-1,117.31 -2016-12-06,fb,se,-1,117.31 -2016-12-06,aapl,sx,1,109.95 -2016-12-06,aapl,le,1,109.95 -2016-12-06,googl,lx,-1,776.18 -2016-12-06,googl,se,-1,776.18 -2016-12-07,fb,sx,1,117.95 -2016-12-07,fb,le,1,117.95 -2016-12-07,googl,sx,1,791.47 -2016-12-07,googl,le,1,791.47 -2016-12-08,nflx,lx,-1,123.24 -2016-12-08,nflx,se,-1,123.24 -2016-12-08,amzn,lx,-1,767.33 -2016-12-08,amzn,se,-1,767.33 -2016-12-09,amzn,sx,1,768.66 -2016-12-09,amzn,le,1,768.66 -2016-12-12,amzn,lx,-1,760.12 -2016-12-12,amzn,se,-1,760.12 -2016-12-12,fb,lx,-1,117.77 -2016-12-12,fb,se,-1,117.77 -2016-12-12,aapl,lx,-1,113.3 -2016-12-12,aapl,se,-1,113.3 -2016-12-12,googl,lx,-1,807.9 -2016-12-12,googl,se,-1,807.9 -2016-12-13,nflx,sx,1,123.78 -2016-12-13,nflx,le,1,123.78 -2016-12-13,amzn,sx,1,774.34 -2016-12-13,amzn,le,1,774.34 -2016-12-13,fb,sx,1,120.31 -2016-12-13,fb,le,1,120.31 -2016-12-13,aapl,sx,1,115.19 -2016-12-13,aapl,le,1,115.19 -2016-12-13,googl,sx,1,815.34 -2016-12-13,googl,le,1,815.34 -2016-12-14,nflx,lx,-1,123.44 -2016-12-14,nflx,se,-1,123.44 -2016-12-14,amzn,lx,-1,768.82 -2016-12-14,amzn,se,-1,768.82 -2016-12-14,fb,lx,-1,120.21 -2016-12-14,fb,se,-1,120.21 -2016-12-15,nflx,sx,1,125.0 -2016-12-15,nflx,le,1,125.0 -2016-12-15,fb,sx,1,120.57 -2016-12-15,fb,le,1,120.57 -2016-12-15,googl,lx,-1,815.65 -2016-12-15,googl,se,-1,815.65 -2016-12-16,nflx,lx,-1,124.22 -2016-12-16,nflx,se,-1,124.22 -2016-12-16,fb,lx,-1,119.87 -2016-12-16,fb,se,-1,119.87 -2016-12-19,nflx,sx,1,125.45 -2016-12-19,nflx,le,1,125.45 -2016-12-19,amzn,sx,1,766.0 -2016-12-19,amzn,le,1,766.0 -2016-12-19,googl,sx,1,812.5 -2016-12-19,googl,le,1,812.5 -2016-12-20,nflx,lx,-1,125.12 -2016-12-20,nflx,se,-1,125.12 -2016-12-21,nflx,sx,1,126.5 -2016-12-21,nflx,le,1,126.5 -2016-12-21,amzn,lx,-1,770.6 -2016-12-21,amzn,se,-1,770.6 -2016-12-21,googl,lx,-1,812.2 -2016-12-21,googl,se,-1,812.2 -2016-12-22,nflx,lx,-1,125.58 -2016-12-22,nflx,se,-1,125.58 -2016-12-22,aapl,lx,-1,116.29 -2016-12-22,aapl,se,-1,116.29 -2016-12-23,nflx,sx,1,125.59 -2016-12-23,nflx,le,1,125.59 -2016-12-23,aapl,sx,1,116.52 -2016-12-23,aapl,le,1,116.52 -2016-12-27,amzn,sx,1,771.4 -2016-12-27,amzn,le,1,771.4 -2016-12-27,fb,sx,1,118.01 -2016-12-27,fb,le,1,118.01 -2016-12-27,googl,sx,1,809.93 -2016-12-27,googl,le,1,809.93 -2016-12-28,nflx,lx,-1,125.89 -2016-12-28,nflx,se,-1,125.89 -2016-12-28,fb,lx,-1,116.92 -2016-12-28,fb,se,-1,116.92 -2016-12-28,aapl,lx,-1,116.76 -2016-12-28,aapl,se,-1,116.76 -2016-12-28,googl,lx,-1,804.57 -2016-12-28,googl,se,-1,804.57 -2016-12-29,amzn,lx,-1,765.15 -2016-12-29,amzn,se,-1,765.15 -2017-01-03,nflx,sx,1,127.49 -2017-01-03,nflx,le,1,127.49 -2017-01-03,amzn,sx,1,753.67 -2017-01-03,amzn,le,1,753.67 -2017-01-03,fb,sx,1,116.86 -2017-01-03,fb,le,1,116.86 -2017-01-03,aapl,sx,1,116.15 -2017-01-03,aapl,le,1,116.15 -2017-01-03,googl,sx,1,808.01 -2017-01-03,googl,le,1,808.01 -2017-01-04,aapl,lx,-1,116.02 -2017-01-04,aapl,se,-1,116.02 -2017-01-04,googl,lx,-1,807.77 -2017-01-04,googl,se,-1,807.77 -2017-01-05,aapl,sx,1,116.61 -2017-01-05,aapl,le,1,116.61 -2017-01-05,googl,sx,1,813.02 -2017-01-05,googl,le,1,813.02 -2017-01-06,nflx,lx,-1,131.07 -2017-01-06,nflx,se,-1,131.07 -2017-01-10,amzn,lx,-1,795.9 -2017-01-10,amzn,se,-1,795.9 -2017-01-10,fb,lx,-1,124.35 -2017-01-10,fb,se,-1,124.35 -2017-01-10,googl,lx,-1,826.01 -2017-01-10,googl,se,-1,826.01 -2017-01-11,nflx,sx,1,130.5 -2017-01-11,nflx,le,1,130.5 -2017-01-11,amzn,sx,1,799.02 -2017-01-11,amzn,le,1,799.02 -2017-01-11,fb,sx,1,126.09 -2017-01-11,fb,le,1,126.09 -2017-01-11,googl,sx,1,829.86 -2017-01-11,googl,le,1,829.86 -2017-01-12,nflx,lx,-1,129.18 -2017-01-12,nflx,se,-1,129.18 -2017-01-12,aapl,lx,-1,119.25 -2017-01-12,aapl,se,-1,119.25 -2017-01-12,googl,lx,-1,829.53 -2017-01-12,googl,se,-1,829.53 -2017-01-13,nflx,sx,1,133.7 -2017-01-13,nflx,le,1,133.7 -2017-01-13,googl,sx,1,830.94 -2017-01-13,googl,le,1,830.94 -2017-01-17,nflx,lx,-1,132.89 -2017-01-17,nflx,se,-1,132.89 -2017-01-17,amzn,lx,-1,809.72 -2017-01-17,amzn,se,-1,809.72 -2017-01-17,fb,lx,-1,127.87 -2017-01-17,fb,se,-1,127.87 -2017-01-17,aapl,sx,1,120.0 -2017-01-17,aapl,le,1,120.0 -2017-01-17,googl,lx,-1,827.46 -2017-01-17,googl,se,-1,827.46 -2017-01-18,nflx,sx,1,133.26 -2017-01-18,nflx,le,1,133.26 -2017-01-18,fb,sx,1,127.92 -2017-01-18,fb,le,1,127.92 -2017-01-18,aapl,lx,-1,119.99 -2017-01-18,aapl,se,-1,119.99 -2017-01-18,googl,sx,1,829.02 -2017-01-18,googl,le,1,829.02 -2017-01-19,amzn,sx,1,809.04 -2017-01-19,amzn,le,1,809.04 -2017-01-19,fb,lx,-1,127.55 -2017-01-19,fb,se,-1,127.55 -2017-01-19,googl,lx,-1,824.37 -2017-01-19,googl,se,-1,824.37 -2017-01-20,amzn,lx,-1,808.33 -2017-01-20,amzn,se,-1,808.33 -2017-01-20,aapl,sx,1,120.0 -2017-01-20,aapl,le,1,120.0 -2017-01-20,googl,sx,1,828.17 -2017-01-20,googl,le,1,828.17 -2017-01-23,nflx,lx,-1,137.39 -2017-01-23,nflx,se,-1,137.39 -2017-01-23,amzn,sx,1,817.88 -2017-01-23,amzn,le,1,817.88 -2017-01-23,fb,sx,1,128.93 -2017-01-23,fb,le,1,128.93 -2017-01-24,nflx,sx,1,140.11 -2017-01-24,nflx,le,1,140.11 -2017-01-24,aapl,lx,-1,119.97 -2017-01-24,aapl,se,-1,119.97 -2017-01-25,nflx,lx,-1,139.52 -2017-01-25,nflx,se,-1,139.52 -2017-01-25,aapl,sx,1,121.88 -2017-01-25,aapl,le,1,121.88 -2017-01-26,googl,lx,-1,856.98 -2017-01-26,googl,se,-1,856.98 -2017-01-27,nflx,sx,1,142.45 -2017-01-27,nflx,le,1,142.45 -2017-01-27,amzn,lx,-1,835.77 -2017-01-27,amzn,se,-1,835.77 -2017-01-27,fb,lx,-1,132.18 -2017-01-27,fb,se,-1,132.18 -2017-01-30,nflx,lx,-1,141.22 -2017-01-30,nflx,se,-1,141.22 -2017-01-30,aapl,lx,-1,121.63 -2017-01-30,aapl,se,-1,121.63 -2017-02-01,nflx,sx,1,140.78 -2017-02-01,nflx,le,1,140.78 -2017-02-01,amzn,sx,1,832.35 -2017-02-01,amzn,le,1,832.35 -2017-02-01,fb,sx,1,133.23 -2017-02-01,fb,le,1,133.23 -2017-02-01,aapl,sx,1,128.75 -2017-02-01,aapl,le,1,128.75 -2017-02-02,nflx,lx,-1,139.2 -2017-02-02,nflx,se,-1,139.2 -2017-02-02,fb,lx,-1,130.84 -2017-02-02,fb,se,-1,130.84 -2017-02-02,aapl,lx,-1,128.53 -2017-02-02,aapl,se,-1,128.53 -2017-02-02,googl,sx,1,818.26 -2017-02-02,googl,le,1,818.26 -2017-02-03,nflx,sx,1,140.25 -2017-02-03,nflx,le,1,140.25 -2017-02-03,amzn,lx,-1,810.2 -2017-02-03,amzn,se,-1,810.2 -2017-02-03,fb,sx,1,130.98 -2017-02-03,fb,le,1,130.98 -2017-02-03,aapl,sx,1,129.08 -2017-02-03,aapl,le,1,129.08 -2017-02-07,amzn,sx,1,812.5 -2017-02-07,amzn,le,1,812.5 -2017-02-07,fb,lx,-1,131.84 -2017-02-07,fb,se,-1,131.84 -2017-02-08,fb,sx,1,134.2 -2017-02-08,fb,le,1,134.2 -2017-02-09,nflx,lx,-1,144.14 -2017-02-09,nflx,se,-1,144.14 -2017-02-09,fb,lx,-1,134.14 -2017-02-09,fb,se,-1,134.14 -2017-02-10,nflx,sx,1,144.82 -2017-02-10,nflx,le,1,144.82 -2017-02-10,fb,sx,1,134.19 -2017-02-10,fb,le,1,134.19 -2017-02-10,aapl,lx,-1,132.12 -2017-02-10,aapl,se,-1,132.12 -2017-02-13,nflx,lx,-1,143.2 -2017-02-13,nflx,se,-1,143.2 -2017-02-13,fb,lx,-1,134.05 -2017-02-13,fb,se,-1,134.05 -2017-02-13,aapl,sx,1,133.29 -2017-02-13,aapl,le,1,133.29 -2017-02-14,amzn,lx,-1,836.39 -2017-02-14,amzn,se,-1,836.39 -2017-02-15,nflx,sx,1,142.27 -2017-02-15,nflx,le,1,142.27 -2017-02-15,amzn,sx,1,842.7 -2017-02-15,amzn,le,1,842.7 -2017-02-15,googl,lx,-1,837.32 -2017-02-15,googl,se,-1,837.32 -2017-02-16,nflx,lx,-1,142.01 -2017-02-16,nflx,se,-1,142.01 -2017-02-16,fb,sx,1,133.84 -2017-02-16,fb,le,1,133.84 -2017-02-16,aapl,lx,-1,135.345 -2017-02-16,aapl,se,-1,135.345 -2017-02-16,googl,sx,1,842.17 -2017-02-16,googl,le,1,842.17 -2017-02-17,nflx,sx,1,142.22 -2017-02-17,nflx,le,1,142.22 -2017-02-17,fb,lx,-1,133.53 -2017-02-17,fb,se,-1,133.53 -2017-02-17,aapl,sx,1,135.72 -2017-02-17,aapl,le,1,135.72 -2017-02-21,fb,sx,1,133.72 -2017-02-21,fb,le,1,133.72 -2017-02-22,amzn,lx,-1,855.61 -2017-02-22,amzn,se,-1,855.61 -2017-02-23,nflx,lx,-1,142.78 -2017-02-23,nflx,se,-1,142.78 -2017-02-23,fb,lx,-1,135.36 -2017-02-23,fb,se,-1,135.36 -2017-02-23,aapl,lx,-1,136.53 -2017-02-23,aapl,se,-1,136.53 -2017-02-23,googl,lx,-1,851.0 -2017-02-23,googl,se,-1,851.0 -2017-02-24,nflx,sx,1,143.25 -2017-02-24,nflx,le,1,143.25 -2017-02-24,fb,sx,1,135.44 -2017-02-24,fb,le,1,135.44 -2017-02-24,aapl,sx,1,136.66 -2017-02-24,aapl,le,1,136.66 -2017-02-27,amzn,sx,1,848.64 -2017-02-27,amzn,le,1,848.64 -2017-02-27,googl,sx,1,849.67 -2017-02-27,googl,le,1,849.67 -2017-02-28,nflx,lx,-1,142.13 -2017-02-28,nflx,se,-1,142.13 -2017-02-28,amzn,lx,-1,845.04 -2017-02-28,amzn,se,-1,845.04 -2017-02-28,fb,lx,-1,135.54 -2017-02-28,fb,se,-1,135.54 -2017-02-28,googl,lx,-1,844.93 -2017-02-28,googl,se,-1,844.93 -2017-03-01,nflx,sx,1,142.65 -2017-03-01,nflx,le,1,142.65 -2017-03-01,amzn,sx,1,853.08 -2017-03-01,amzn,le,1,853.08 -2017-03-01,fb,sx,1,137.42 -2017-03-01,fb,le,1,137.42 -2017-03-01,googl,sx,1,856.75 -2017-03-01,googl,le,1,856.75 -2017-03-02,nflx,lx,-1,139.53 -2017-03-02,nflx,se,-1,139.53 -2017-03-02,amzn,lx,-1,848.91 -2017-03-02,amzn,se,-1,848.91 -2017-03-02,fb,lx,-1,136.76 -2017-03-02,fb,se,-1,136.76 -2017-03-02,aapl,lx,-1,138.96 -2017-03-02,aapl,se,-1,138.96 -2017-03-02,googl,lx,-1,849.85 -2017-03-02,googl,se,-1,849.85 -2017-03-03,amzn,sx,1,849.88 -2017-03-03,amzn,le,1,849.88 -2017-03-03,fb,sx,1,137.17 -2017-03-03,fb,le,1,137.17 -2017-03-03,aapl,sx,1,139.78 -2017-03-03,aapl,le,1,139.78 -2017-03-06,nflx,sx,1,141.94 -2017-03-06,nflx,le,1,141.94 -2017-03-06,amzn,lx,-1,846.61 -2017-03-06,amzn,se,-1,846.61 -2017-03-06,aapl,lx,-1,139.34 -2017-03-06,aapl,se,-1,139.34 -2017-03-07,nflx,lx,-1,141.43 -2017-03-07,nflx,se,-1,141.43 -2017-03-07,fb,lx,-1,137.3 -2017-03-07,fb,se,-1,137.3 -2017-03-07,aapl,sx,1,139.52 -2017-03-07,aapl,le,1,139.52 -2017-03-07,googl,sx,1,851.15 -2017-03-07,googl,le,1,851.15 -2017-03-08,amzn,sx,1,850.5 -2017-03-08,amzn,le,1,850.5 -2017-03-08,fb,sx,1,137.72 -2017-03-08,fb,le,1,137.72 -2017-03-08,aapl,lx,-1,139.0 -2017-03-08,aapl,se,-1,139.0 -2017-03-09,nflx,sx,1,140.53 -2017-03-09,nflx,le,1,140.53 -2017-03-10,amzn,lx,-1,852.46 -2017-03-10,amzn,se,-1,852.46 -2017-03-10,aapl,sx,1,139.14 -2017-03-10,aapl,le,1,139.14 -2017-03-13,amzn,sx,1,854.59 -2017-03-13,amzn,le,1,854.59 -2017-03-14,nflx,lx,-1,143.19 -2017-03-14,nflx,se,-1,143.19 -2017-03-14,amzn,lx,-1,852.53 -2017-03-14,amzn,se,-1,852.53 -2017-03-14,fb,lx,-1,139.32 -2017-03-14,fb,se,-1,139.32 -2017-03-14,aapl,lx,-1,138.99 -2017-03-14,aapl,se,-1,138.99 -2017-03-15,nflx,sx,1,145.25 -2017-03-15,nflx,le,1,145.25 -2017-03-15,amzn,sx,1,852.97 -2017-03-15,amzn,le,1,852.97 -2017-03-15,fb,sx,1,139.72 -2017-03-15,fb,le,1,139.72 -2017-03-15,aapl,sx,1,140.46 -2017-03-15,aapl,le,1,140.46 -2017-03-16,nflx,lx,-1,144.39 -2017-03-16,nflx,se,-1,144.39 -2017-03-17,nflx,sx,1,145.11 -2017-03-17,nflx,le,1,145.11 -2017-03-17,amzn,lx,-1,852.31 -2017-03-17,amzn,se,-1,852.31 -2017-03-17,fb,lx,-1,139.84 -2017-03-17,fb,se,-1,139.84 -2017-03-17,aapl,lx,-1,139.99 -2017-03-17,aapl,se,-1,139.99 -2017-03-20,amzn,sx,1,856.97 -2017-03-20,amzn,le,1,856.97 -2017-03-20,fb,sx,1,139.94 -2017-03-20,fb,le,1,139.94 -2017-03-20,aapl,sx,1,141.46 -2017-03-20,aapl,le,1,141.46 -2017-03-20,googl,lx,-1,867.91 -2017-03-20,googl,se,-1,867.91 -2017-03-21,nflx,lx,-1,142.42 -2017-03-21,nflx,se,-1,142.42 -2017-03-21,amzn,lx,-1,843.2 -2017-03-21,amzn,se,-1,843.2 -2017-03-21,fb,lx,-1,138.51 -2017-03-21,fb,se,-1,138.51 -2017-03-21,aapl,lx,-1,139.84 -2017-03-21,aapl,se,-1,139.84 -2017-03-22,nflx,sx,1,142.65 -2017-03-22,nflx,le,1,142.65 -2017-03-22,amzn,sx,1,848.06 -2017-03-22,amzn,le,1,848.06 -2017-03-22,fb,sx,1,139.59 -2017-03-22,fb,le,1,139.59 -2017-03-22,aapl,sx,1,141.42 -2017-03-22,aapl,le,1,141.42 -2017-03-23,nflx,lx,-1,141.84 -2017-03-23,nflx,se,-1,141.84 -2017-03-23,amzn,lx,-1,847.38 -2017-03-23,amzn,se,-1,847.38 -2017-03-23,fb,lx,-1,139.53 -2017-03-23,fb,se,-1,139.53 -2017-03-23,aapl,lx,-1,140.92 -2017-03-23,aapl,se,-1,140.92 -2017-03-24,nflx,sx,1,142.02 -2017-03-24,nflx,le,1,142.02 -2017-03-24,fb,sx,1,140.34 -2017-03-24,fb,le,1,140.34 -2017-03-27,amzn,sx,1,846.82 -2017-03-27,amzn,le,1,846.82 -2017-03-27,fb,lx,-1,140.32 -2017-03-27,fb,se,-1,140.32 -2017-03-27,aapl,sx,1,140.88 -2017-03-27,aapl,le,1,140.88 -2017-03-27,googl,sx,1,838.51 -2017-03-27,googl,le,1,838.51 -2017-03-28,fb,sx,1,141.76 -2017-03-28,fb,le,1,141.76 -2017-03-30,fb,lx,-1,142.41 -2017-03-30,fb,se,-1,142.41 -2017-03-30,aapl,lx,-1,143.93 -2017-03-30,aapl,se,-1,143.93 -2017-03-30,googl,lx,-1,849.48 -2017-03-30,googl,se,-1,849.48 -2017-03-31,nflx,lx,-1,147.81 -2017-03-31,nflx,se,-1,147.81 -2017-04-03,fb,sx,1,142.28 -2017-04-03,fb,le,1,142.28 -2017-04-03,aapl,sx,1,143.7 -2017-04-03,aapl,le,1,143.7 -2017-04-03,googl,sx,1,856.75 -2017-04-03,googl,le,1,856.75 -2017-04-04,fb,lx,-1,141.73 -2017-04-04,fb,se,-1,141.73 -2017-04-04,googl,lx,-1,852.57 -2017-04-04,googl,se,-1,852.57 -2017-04-05,fb,sx,1,141.85 -2017-04-05,fb,le,1,141.85 -2017-04-05,aapl,lx,-1,144.02 -2017-04-05,aapl,se,-1,144.02 -2017-04-06,nflx,sx,1,143.74 -2017-04-06,nflx,le,1,143.74 -2017-04-06,amzn,lx,-1,898.28 -2017-04-06,amzn,se,-1,898.28 -2017-04-06,fb,lx,-1,141.17 -2017-04-06,fb,se,-1,141.17 -2017-04-07,nflx,lx,-1,143.11 -2017-04-07,nflx,se,-1,143.11 -2017-04-10,nflx,sx,1,143.85 -2017-04-10,nflx,le,1,143.85 -2017-04-10,amzn,sx,1,907.04 -2017-04-10,amzn,le,1,907.04 -2017-04-10,fb,sx,1,141.04 -2017-04-10,fb,le,1,141.04 -2017-04-11,amzn,lx,-1,902.36 -2017-04-11,amzn,se,-1,902.36 -2017-04-11,fb,lx,-1,139.92 -2017-04-11,fb,se,-1,139.92 -2017-04-12,nflx,lx,-1,143.83 -2017-04-12,nflx,se,-1,143.83 -2017-04-12,aapl,sx,1,141.8 -2017-04-12,aapl,le,1,141.8 -2017-04-12,googl,sx,1,841.46 -2017-04-12,googl,le,1,841.46 -2017-04-13,aapl,lx,-1,141.05 -2017-04-13,aapl,se,-1,141.05 -2017-04-13,googl,lx,-1,840.18 -2017-04-13,googl,se,-1,840.18 -2017-04-17,nflx,sx,1,147.25 -2017-04-17,nflx,le,1,147.25 -2017-04-17,amzn,sx,1,901.99 -2017-04-17,amzn,le,1,901.99 -2017-04-17,fb,sx,1,141.42 -2017-04-17,fb,le,1,141.42 -2017-04-17,aapl,sx,1,141.83 -2017-04-17,aapl,le,1,141.83 -2017-04-17,googl,sx,1,855.13 -2017-04-17,googl,le,1,855.13 -2017-04-18,nflx,lx,-1,143.36 -2017-04-18,nflx,se,-1,143.36 -2017-04-18,fb,lx,-1,140.96 -2017-04-18,fb,se,-1,140.96 -2017-04-18,aapl,lx,-1,141.2 -2017-04-18,aapl,se,-1,141.2 -2017-04-18,googl,lx,-1,853.99 -2017-04-18,googl,se,-1,853.99 -2017-04-19,amzn,lx,-1,899.2 -2017-04-19,amzn,se,-1,899.2 -2017-04-19,fb,sx,1,142.27 -2017-04-19,fb,le,1,142.27 -2017-04-19,googl,sx,1,856.51 -2017-04-19,googl,le,1,856.51 -2017-04-20,nflx,sx,1,141.18 -2017-04-20,nflx,le,1,141.18 -2017-04-20,amzn,sx,1,902.06 -2017-04-20,amzn,le,1,902.06 -2017-04-20,aapl,sx,1,142.44 -2017-04-20,aapl,le,1,142.44 -2017-04-21,amzn,lx,-1,898.53 -2017-04-21,amzn,se,-1,898.53 -2017-04-21,fb,lx,-1,143.68 -2017-04-21,fb,se,-1,143.68 -2017-04-21,aapl,lx,-1,142.27 -2017-04-21,aapl,se,-1,142.27 -2017-04-21,googl,lx,-1,858.95 -2017-04-21,googl,se,-1,858.95 -2017-04-24,amzn,sx,1,907.41 -2017-04-24,amzn,le,1,907.41 -2017-04-24,fb,sx,1,145.47 -2017-04-24,fb,le,1,145.47 -2017-04-24,aapl,sx,1,143.64 -2017-04-24,aapl,le,1,143.64 -2017-04-24,googl,sx,1,878.93 -2017-04-24,googl,le,1,878.93 -2017-04-26,nflx,lx,-1,150.17 -2017-04-26,nflx,se,-1,150.17 -2017-04-26,aapl,lx,-1,143.6508 -2017-04-26,aapl,se,-1,143.6508 -2017-04-27,nflx,sx,1,153.08 -2017-04-27,nflx,le,1,153.08 -2017-04-27,aapl,sx,1,143.79 -2017-04-27,aapl,le,1,143.79 -2017-04-28,nflx,lx,-1,152.2 -2017-04-28,nflx,se,-1,152.2 -2017-04-28,aapl,lx,-1,143.65 -2017-04-28,aapl,se,-1,143.65 -2017-05-01,nflx,sx,1,155.35 -2017-05-01,nflx,le,1,155.35 -2017-05-01,aapl,sx,1,146.6 -2017-05-01,aapl,le,1,146.6 -2017-05-02,amzn,lx,-1,946.68 -2017-05-02,amzn,se,-1,946.68 -2017-05-03,nflx,lx,-1,155.59 -2017-05-03,nflx,se,-1,155.59 -2017-05-03,fb,lx,-1,151.8 -2017-05-03,fb,se,-1,151.8 -2017-05-03,aapl,lx,-1,147.06 -2017-05-03,aapl,se,-1,147.06 -2017-05-04,nflx,sx,1,157.25 -2017-05-04,nflx,le,1,157.25 -2017-05-05,nflx,lx,-1,156.6 -2017-05-05,nflx,se,-1,156.6 -2017-05-05,aapl,sx,1,148.96 -2017-05-05,aapl,le,1,148.96 -2017-05-05,googl,lx,-1,950.28 -2017-05-05,googl,se,-1,950.28 -2017-05-08,amzn,sx,1,949.04 -2017-05-08,amzn,le,1,949.04 -2017-05-08,fb,sx,1,151.06 -2017-05-08,fb,le,1,151.06 -2017-05-08,googl,sx,1,958.69 -2017-05-08,googl,le,1,958.69 -2017-05-09,nflx,sx,1,157.46 -2017-05-09,nflx,le,1,157.46 -2017-05-09,fb,lx,-1,150.48 -2017-05-09,fb,se,-1,150.48 -2017-05-09,googl,lx,-1,956.71 -2017-05-09,googl,se,-1,956.71 -2017-05-10,amzn,lx,-1,948.95 -2017-05-10,amzn,se,-1,948.95 -2017-05-10,aapl,lx,-1,153.26 -2017-05-10,aapl,se,-1,153.26 -2017-05-11,nflx,lx,-1,158.54 -2017-05-11,nflx,se,-1,158.54 -2017-05-11,aapl,sx,1,153.95 -2017-05-11,aapl,le,1,153.95 -2017-05-11,googl,sx,1,955.89 -2017-05-11,googl,le,1,955.89 -2017-05-12,nflx,sx,1,160.81 -2017-05-12,nflx,le,1,160.81 -2017-05-12,amzn,sx,1,961.35 -2017-05-12,amzn,le,1,961.35 -2017-05-12,fb,sx,1,150.33 -2017-05-12,fb,le,1,150.33 -2017-05-12,googl,lx,-1,955.14 -2017-05-12,googl,se,-1,955.14 -2017-05-15,nflx,lx,-1,160.02 -2017-05-15,nflx,se,-1,160.02 -2017-05-15,amzn,lx,-1,957.97 -2017-05-15,amzn,se,-1,957.97 -2017-05-15,fb,lx,-1,150.19 -2017-05-15,fb,se,-1,150.19 -2017-05-15,aapl,lx,-1,155.7 -2017-05-15,aapl,se,-1,155.7 -2017-05-15,googl,sx,1,959.22 -2017-05-15,googl,le,1,959.22 -2017-05-16,amzn,sx,1,966.07 -2017-05-16,amzn,le,1,966.07 -2017-05-17,amzn,lx,-1,944.52 -2017-05-17,amzn,se,-1,944.52 -2017-05-17,googl,lx,-1,942.17 -2017-05-17,googl,se,-1,942.17 -2017-05-18,nflx,sx,1,155.7 -2017-05-18,nflx,le,1,155.7 -2017-05-18,amzn,sx,1,958.49 -2017-05-18,amzn,le,1,958.49 -2017-05-18,fb,sx,1,147.66 -2017-05-18,fb,le,1,147.66 -2017-05-18,aapl,sx,1,152.54 -2017-05-18,aapl,le,1,152.54 -2017-05-18,googl,sx,1,950.5 -2017-05-18,googl,le,1,950.5 -2017-05-23,fb,lx,-1,148.07 -2017-05-23,fb,se,-1,148.07 -2017-05-23,aapl,lx,-1,153.8 -2017-05-23,aapl,se,-1,153.8 -2017-05-24,nflx,lx,-1,157.75 -2017-05-24,nflx,se,-1,157.75 -2017-05-24,fb,sx,1,150.04 -2017-05-24,fb,le,1,150.04 -2017-05-25,nflx,sx,1,163.05 -2017-05-25,nflx,le,1,163.05 -2017-05-25,aapl,sx,1,153.87 -2017-05-25,aapl,le,1,153.87 -2017-05-26,nflx,lx,-1,162.43 -2017-05-26,nflx,se,-1,162.43 -2017-05-26,aapl,lx,-1,153.61 -2017-05-26,aapl,se,-1,153.61 -2017-05-30,nflx,sx,1,163.22 -2017-05-30,nflx,le,1,163.22 -2017-05-30,aapl,sx,1,153.67 -2017-05-30,aapl,le,1,153.67 -2017-05-31,nflx,lx,-1,163.07 -2017-05-31,nflx,se,-1,163.07 -2017-05-31,amzn,lx,-1,994.62 -2017-05-31,amzn,se,-1,994.62 -2017-05-31,fb,lx,-1,151.46 -2017-05-31,fb,se,-1,151.46 -2017-05-31,aapl,lx,-1,152.76 -2017-05-31,aapl,se,-1,152.76 -2017-05-31,googl,lx,-1,987.09 -2017-05-31,googl,se,-1,987.09 -2017-06-01,amzn,sx,1,995.95 -2017-06-01,amzn,le,1,995.95 -2017-06-01,fb,sx,1,151.53 -2017-06-01,fb,le,1,151.53 -2017-06-01,aapl,sx,1,153.18 -2017-06-01,aapl,le,1,153.18 -2017-06-01,googl,sx,1,988.29 -2017-06-01,googl,le,1,988.29 -2017-06-02,nflx,sx,1,165.18 -2017-06-02,nflx,le,1,165.18 -2017-06-05,nflx,lx,-1,165.06 -2017-06-05,nflx,se,-1,165.06 -2017-06-05,aapl,lx,-1,153.93 -2017-06-05,aapl,se,-1,153.93 -2017-06-06,nflx,sx,1,165.17 -2017-06-06,nflx,le,1,165.17 -2017-06-06,amzn,lx,-1,1002.97 -2017-06-06,amzn,se,-1,1002.97 -2017-06-06,fb,lx,-1,152.81 -2017-06-06,fb,se,-1,152.81 -2017-06-06,aapl,sx,1,154.45 -2017-06-06,aapl,le,1,154.45 -2017-06-06,googl,lx,-1,996.68 -2017-06-06,googl,se,-1,996.68 -2017-06-07,amzn,sx,1,1010.07 -2017-06-07,amzn,le,1,1010.07 -2017-06-07,fb,sx,1,153.12 -2017-06-07,fb,le,1,153.12 -2017-06-07,googl,sx,1,1001.59 -2017-06-07,googl,le,1,1001.59 -2017-06-08,amzn,lx,-1,1009.94 -2017-06-08,amzn,se,-1,1009.94 -2017-06-08,aapl,lx,-1,154.99 -2017-06-08,aapl,se,-1,154.99 -2017-06-09,nflx,lx,-1,158.03 -2017-06-09,nflx,se,-1,158.03 -2017-06-09,fb,lx,-1,149.63 -2017-06-09,fb,se,-1,149.63 -2017-06-09,googl,lx,-1,970.12 -2017-06-09,googl,se,-1,970.12 -2017-06-13,nflx,sx,1,152.72 -2017-06-13,nflx,le,1,152.72 -2017-06-13,amzn,sx,1,980.79 -2017-06-13,amzn,le,1,980.79 -2017-06-13,fb,sx,1,150.68 -2017-06-13,fb,le,1,150.68 -2017-06-13,aapl,sx,1,146.59 -2017-06-13,aapl,le,1,146.59 -2017-06-13,googl,sx,1,970.5 -2017-06-13,googl,le,1,970.5 -2017-06-14,nflx,lx,-1,152.2 -2017-06-14,nflx,se,-1,152.2 -2017-06-14,amzn,lx,-1,976.47 -2017-06-14,amzn,se,-1,976.47 -2017-06-14,fb,lx,-1,150.25 -2017-06-14,fb,se,-1,150.25 -2017-06-14,aapl,lx,-1,145.16 -2017-06-14,aapl,se,-1,145.16 -2017-06-14,googl,lx,-1,967.93 -2017-06-14,googl,se,-1,967.93 -2017-06-16,nflx,sx,1,152.38 -2017-06-16,nflx,le,1,152.38 -2017-06-16,amzn,sx,1,987.71 -2017-06-16,amzn,le,1,987.71 -2017-06-16,fb,sx,1,150.64 -2017-06-16,fb,le,1,150.64 -2017-06-19,aapl,sx,1,146.34 -2017-06-19,aapl,le,1,146.34 -2017-06-19,googl,sx,1,975.22 -2017-06-19,googl,le,1,975.22 -2017-06-20,nflx,lx,-1,152.05 -2017-06-20,nflx,se,-1,152.05 -2017-06-20,amzn,lx,-1,992.59 -2017-06-20,amzn,se,-1,992.59 -2017-06-20,fb,lx,-1,152.25 -2017-06-20,fb,se,-1,152.25 -2017-06-20,aapl,lx,-1,145.01 -2017-06-20,aapl,se,-1,145.01 -2017-06-20,googl,lx,-1,968.99 -2017-06-20,googl,se,-1,968.99 -2017-06-21,nflx,sx,1,155.03 -2017-06-21,nflx,le,1,155.03 -2017-06-21,amzn,sx,1,1002.23 -2017-06-21,amzn,le,1,1002.23 -2017-06-21,fb,sx,1,153.91 -2017-06-21,fb,le,1,153.91 -2017-06-21,aapl,sx,1,145.87 -2017-06-21,aapl,le,1,145.87 -2017-06-21,googl,sx,1,978.59 -2017-06-21,googl,le,1,978.59 -2017-06-22,nflx,lx,-1,154.89 -2017-06-22,nflx,se,-1,154.89 -2017-06-22,amzn,lx,-1,1001.3 -2017-06-22,amzn,se,-1,1001.3 -2017-06-22,fb,lx,-1,153.4 -2017-06-22,fb,se,-1,153.4 -2017-06-22,aapl,lx,-1,145.63 -2017-06-22,aapl,se,-1,145.63 -2017-06-22,googl,lx,-1,976.62 -2017-06-22,googl,se,-1,976.62 -2017-06-23,nflx,sx,1,158.03 -2017-06-23,nflx,le,1,158.03 -2017-06-23,amzn,sx,1,1003.74 -2017-06-23,amzn,le,1,1003.74 -2017-06-23,fb,sx,1,155.07 -2017-06-23,fb,le,1,155.07 -2017-06-23,aapl,sx,1,146.35 -2017-06-23,aapl,le,1,146.35 -2017-06-23,googl,sx,1,986.09 -2017-06-23,googl,le,1,986.09 -2017-06-26,nflx,lx,-1,157.5 -2017-06-26,nflx,se,-1,157.5 -2017-06-26,amzn,lx,-1,993.98 -2017-06-26,amzn,se,-1,993.98 -2017-06-26,fb,lx,-1,153.59 -2017-06-26,fb,se,-1,153.59 -2017-06-26,aapl,lx,-1,145.82 -2017-06-26,aapl,se,-1,145.82 -2017-06-26,googl,lx,-1,972.09 -2017-06-26,googl,se,-1,972.09 -2017-06-28,nflx,sx,1,153.41 -2017-06-28,nflx,le,1,153.41 -2017-06-28,amzn,sx,1,990.33 -2017-06-28,amzn,le,1,990.33 -2017-06-28,fb,sx,1,153.24 -2017-06-28,fb,le,1,153.24 -2017-06-28,aapl,sx,1,145.83 -2017-06-28,aapl,le,1,145.83 -2017-06-28,googl,sx,1,961.01 -2017-06-28,googl,le,1,961.01 -2017-06-29,nflx,lx,-1,150.09 -2017-06-29,nflx,se,-1,150.09 -2017-06-29,amzn,lx,-1,975.93 -2017-06-29,amzn,se,-1,975.93 -2017-06-29,fb,lx,-1,151.04 -2017-06-29,fb,se,-1,151.04 -2017-06-29,aapl,lx,-1,143.68 -2017-06-29,aapl,se,-1,143.68 -2017-06-29,googl,lx,-1,937.82 -2017-06-29,googl,se,-1,937.82 -2017-06-30,aapl,sx,1,144.02 -2017-06-30,aapl,le,1,144.02 -2017-07-03,aapl,lx,-1,143.5 -2017-07-03,aapl,se,-1,143.5 -2017-07-05,nflx,sx,1,147.61 -2017-07-05,nflx,le,1,147.61 -2017-07-05,amzn,sx,1,971.4 -2017-07-05,amzn,le,1,971.4 -2017-07-05,fb,sx,1,150.34 -2017-07-05,fb,le,1,150.34 -2017-07-05,aapl,sx,1,144.09 -2017-07-05,aapl,le,1,144.09 -2017-07-05,googl,sx,1,932.26 -2017-07-05,googl,le,1,932.26 -2017-07-06,nflx,lx,-1,146.25 -2017-07-06,nflx,se,-1,146.25 -2017-07-06,amzn,lx,-1,965.14 -2017-07-06,amzn,se,-1,965.14 -2017-07-06,fb,lx,-1,148.82 -2017-07-06,fb,se,-1,148.82 -2017-07-06,aapl,lx,-1,142.73 -2017-07-06,aapl,se,-1,142.73 -2017-07-06,googl,lx,-1,927.69 -2017-07-06,googl,se,-1,927.69 -2017-07-07,nflx,sx,1,150.18 -2017-07-07,nflx,le,1,150.18 -2017-07-07,amzn,sx,1,978.76 -2017-07-07,amzn,le,1,978.76 -2017-07-07,fb,sx,1,151.44 -2017-07-07,fb,le,1,151.44 -2017-07-07,aapl,sx,1,144.18 -2017-07-07,aapl,le,1,144.18 -2017-07-07,googl,sx,1,940.81 -2017-07-07,googl,le,1,940.81 -2017-07-11,amzn,lx,-1,994.13 -2017-07-11,amzn,se,-1,994.13 -2017-07-12,amzn,sx,1,1006.51 -2017-07-12,amzn,le,1,1006.51 -2017-07-13,nflx,lx,-1,158.17 -2017-07-13,nflx,se,-1,158.17 -2017-07-13,amzn,lx,-1,999.855 -2017-07-13,amzn,se,-1,999.855 -2017-07-14,nflx,sx,1,161.12 -2017-07-14,nflx,le,1,161.12 -2017-07-14,amzn,sx,1,1001.81 -2017-07-14,amzn,le,1,1001.81 -2017-07-17,fb,lx,-1,159.73 -2017-07-17,fb,se,-1,159.73 -2017-07-17,googl,lx,-1,975.96 -2017-07-17,googl,se,-1,975.96 -2017-07-18,fb,sx,1,162.86 -2017-07-18,fb,le,1,162.86 -2017-07-18,googl,sx,1,986.95 -2017-07-18,googl,le,1,986.95 -2017-07-20,nflx,lx,-1,183.6 -2017-07-20,nflx,se,-1,183.6 -2017-07-20,aapl,lx,-1,150.34 -2017-07-20,aapl,se,-1,150.34 -2017-07-20,googl,lx,-1,992.19 -2017-07-20,googl,se,-1,992.19 -2017-07-21,nflx,sx,1,188.54 -2017-07-21,nflx,le,1,188.54 -2017-07-21,amzn,lx,-1,1025.67 -2017-07-21,amzn,se,-1,1025.67 -2017-07-21,fb,lx,-1,164.43 -2017-07-21,fb,se,-1,164.43 -2017-07-21,googl,sx,1,993.84 -2017-07-21,googl,le,1,993.84 -2017-07-24,nflx,lx,-1,187.91 -2017-07-24,nflx,se,-1,187.91 -2017-07-24,amzn,sx,1,1038.95 -2017-07-24,amzn,le,1,1038.95 -2017-07-24,fb,sx,1,166.0 -2017-07-24,fb,le,1,166.0 -2017-07-24,aapl,sx,1,152.09 -2017-07-24,aapl,le,1,152.09 -2017-07-25,fb,lx,-1,165.28 -2017-07-25,fb,se,-1,165.28 -2017-07-25,googl,lx,-1,969.03 -2017-07-25,googl,se,-1,969.03 -2017-07-26,nflx,sx,1,189.08 -2017-07-26,nflx,le,1,189.08 -2017-07-26,fb,sx,1,165.61 -2017-07-26,fb,le,1,165.61 -2017-07-27,nflx,lx,-1,182.68 -2017-07-27,nflx,se,-1,182.68 -2017-07-27,amzn,lx,-1,1046.0 -2017-07-27,amzn,se,-1,1046.0 -2017-07-27,aapl,lx,-1,150.56 -2017-07-27,aapl,se,-1,150.56 -2017-07-28,nflx,sx,1,184.04 -2017-07-28,nflx,le,1,184.04 -2017-07-28,googl,sx,1,958.33 -2017-07-28,googl,le,1,958.33 -2017-07-31,nflx,lx,-1,181.66 -2017-07-31,nflx,se,-1,181.66 -2017-07-31,fb,lx,-1,169.25 -2017-07-31,fb,se,-1,169.25 -2017-07-31,googl,lx,-1,945.5 -2017-07-31,googl,se,-1,945.5 -2017-08-01,nflx,sx,1,182.03 -2017-08-01,nflx,le,1,182.03 -2017-08-01,amzn,sx,1,996.19 -2017-08-01,amzn,le,1,996.19 -2017-08-01,fb,sx,1,169.86 -2017-08-01,fb,le,1,169.86 -2017-08-01,aapl,sx,1,150.05 -2017-08-01,aapl,le,1,150.05 -2017-08-01,googl,sx,1,946.56 -2017-08-01,googl,le,1,946.56 -2017-08-02,nflx,lx,-1,180.74 -2017-08-02,nflx,se,-1,180.74 -2017-08-02,amzn,lx,-1,995.89 -2017-08-02,amzn,se,-1,995.89 -2017-08-02,fb,lx,-1,169.25 -2017-08-02,fb,se,-1,169.25 -2017-08-03,aapl,lx,-1,155.57 -2017-08-03,aapl,se,-1,155.57 -2017-08-03,googl,lx,-1,940.3 -2017-08-03,googl,se,-1,940.3 -2017-08-04,nflx,sx,1,180.27 -2017-08-04,nflx,le,1,180.27 -2017-08-04,amzn,sx,1,987.58 -2017-08-04,amzn,le,1,987.58 -2017-08-04,fb,sx,1,169.62 -2017-08-04,fb,le,1,169.62 -2017-08-04,aapl,sx,1,156.39 -2017-08-04,aapl,le,1,156.39 -2017-08-04,googl,sx,1,945.79 -2017-08-04,googl,le,1,945.79 -2017-08-07,googl,lx,-1,945.75 -2017-08-07,googl,se,-1,945.75 -2017-08-08,nflx,lx,-1,178.36 -2017-08-08,nflx,se,-1,178.36 -2017-08-08,fb,lx,-1,171.23 -2017-08-08,fb,se,-1,171.23 -2017-08-09,amzn,lx,-1,982.01 -2017-08-09,amzn,se,-1,982.01 -2017-08-10,aapl,lx,-1,155.27 -2017-08-10,aapl,se,-1,155.27 -2017-08-11,nflx,sx,1,171.4 -2017-08-11,nflx,le,1,171.4 -2017-08-11,amzn,sx,1,967.99 -2017-08-11,amzn,le,1,967.99 -2017-08-11,fb,sx,1,168.08 -2017-08-11,fb,le,1,168.08 -2017-08-11,aapl,sx,1,157.48 -2017-08-11,aapl,le,1,157.48 -2017-08-11,googl,sx,1,930.09 -2017-08-11,googl,le,1,930.09 -2017-08-14,nflx,lx,-1,171.0 -2017-08-14,nflx,se,-1,171.0 -2017-08-15,amzn,lx,-1,982.74 -2017-08-15,amzn,se,-1,982.74 -2017-08-15,googl,lx,-1,938.08 -2017-08-15,googl,se,-1,938.08 -2017-08-16,nflx,sx,1,169.98 -2017-08-16,nflx,le,1,169.98 -2017-08-16,fb,lx,-1,170.0 -2017-08-16,fb,se,-1,170.0 -2017-08-16,aapl,lx,-1,160.95 -2017-08-16,aapl,se,-1,160.95 -2017-08-16,googl,sx,1,944.27 -2017-08-16,googl,le,1,944.27 -2017-08-17,nflx,lx,-1,166.09 -2017-08-17,nflx,se,-1,166.09 -2017-08-17,googl,lx,-1,927.66 -2017-08-17,googl,se,-1,927.66 -2017-08-18,nflx,sx,1,166.54 -2017-08-18,nflx,le,1,166.54 -2017-08-18,fb,sx,1,167.41 -2017-08-18,fb,le,1,167.41 -2017-08-22,amzn,sx,1,966.9 -2017-08-22,amzn,le,1,966.9 -2017-08-22,aapl,sx,1,159.78 -2017-08-22,aapl,le,1,159.78 -2017-08-22,googl,sx,1,940.4 -2017-08-22,googl,le,1,940.4 -2017-08-23,nflx,lx,-1,169.06 -2017-08-23,nflx,se,-1,169.06 -2017-08-23,amzn,lx,-1,958.0 -2017-08-23,amzn,se,-1,958.0 -2017-08-23,fb,lx,-1,168.71 -2017-08-23,fb,se,-1,168.71 -2017-08-24,aapl,lx,-1,159.27 -2017-08-24,aapl,se,-1,159.27 -2017-08-24,googl,lx,-1,936.89 -2017-08-24,googl,se,-1,936.89 -2017-08-25,aapl,sx,1,159.86 -2017-08-25,aapl,le,1,159.86 -2017-08-28,nflx,sx,1,167.12 -2017-08-28,nflx,le,1,167.12 -2017-08-28,amzn,sx,1,946.02 -2017-08-28,amzn,le,1,946.02 -2017-08-28,fb,sx,1,167.24 -2017-08-28,fb,le,1,167.24 -2017-08-29,googl,sx,1,935.75 -2017-08-29,googl,le,1,935.75 -2017-09-01,amzn,lx,-1,978.25 -2017-09-01,amzn,se,-1,978.25 -2017-09-01,googl,lx,-1,951.99 -2017-09-01,googl,se,-1,951.99 -2017-09-05,nflx,lx,-1,174.52 -2017-09-05,nflx,se,-1,174.52 -2017-09-05,fb,lx,-1,170.72 -2017-09-05,fb,se,-1,170.72 -2017-09-05,aapl,lx,-1,162.08 -2017-09-05,aapl,se,-1,162.08 -2017-09-06,nflx,sx,1,179.25 -2017-09-06,nflx,le,1,179.25 -2017-09-06,amzn,sx,1,967.8 -2017-09-06,amzn,le,1,967.8 -2017-09-06,fb,sx,1,172.09 -2017-09-06,fb,le,1,172.09 -2017-09-06,googl,sx,1,942.02 -2017-09-06,googl,le,1,942.02 -2017-09-07,nflx,lx,-1,179.0 -2017-09-07,nflx,se,-1,179.0 -2017-09-08,amzn,lx,-1,965.9 -2017-09-08,amzn,se,-1,965.9 -2017-09-08,fb,lx,-1,170.95 -2017-09-08,fb,se,-1,170.95 -2017-09-08,googl,lx,-1,941.41 -2017-09-08,googl,se,-1,941.41 -2017-09-11,nflx,sx,1,181.74 -2017-09-11,nflx,le,1,181.74 -2017-09-11,amzn,sx,1,977.96 -2017-09-11,amzn,le,1,977.96 -2017-09-11,fb,sx,1,173.51 -2017-09-11,fb,le,1,173.51 -2017-09-11,aapl,sx,1,161.5 -2017-09-11,aapl,le,1,161.5 -2017-09-11,googl,sx,1,943.29 -2017-09-11,googl,le,1,943.29 -2017-09-12,fb,lx,-1,172.96 -2017-09-12,fb,se,-1,172.96 -2017-09-12,aapl,lx,-1,160.82 -2017-09-12,aapl,se,-1,160.82 -2017-09-13,nflx,lx,-1,183.64 -2017-09-13,nflx,se,-1,183.64 -2017-09-13,fb,sx,1,173.05 -2017-09-13,fb,le,1,173.05 -2017-09-14,amzn,lx,-1,992.21 -2017-09-14,amzn,se,-1,992.21 -2017-09-14,fb,lx,-1,170.96 -2017-09-14,fb,se,-1,170.96 -2017-09-14,googl,lx,-1,940.13 -2017-09-14,googl,se,-1,940.13 -2017-09-15,fb,sx,1,171.58 -2017-09-15,fb,le,1,171.58 -2017-09-15,aapl,sx,1,159.88 -2017-09-15,aapl,le,1,159.88 -2017-09-18,nflx,sx,1,184.62 -2017-09-18,nflx,le,1,184.62 -2017-09-18,fb,lx,-1,170.01 -2017-09-18,fb,se,-1,170.01 -2017-09-18,aapl,lx,-1,158.67 -2017-09-18,aapl,se,-1,158.67 -2017-09-19,fb,sx,1,172.52 -2017-09-19,fb,le,1,172.52 -2017-09-19,aapl,sx,1,158.73 -2017-09-19,aapl,le,1,158.73 -2017-09-19,googl,sx,1,936.86 -2017-09-19,googl,le,1,936.86 -2017-09-20,nflx,lx,-1,185.51 -2017-09-20,nflx,se,-1,185.51 -2017-09-20,amzn,sx,1,973.21 -2017-09-20,amzn,le,1,973.21 -2017-09-20,fb,lx,-1,172.17 -2017-09-20,fb,se,-1,172.17 -2017-09-20,aapl,lx,-1,156.07 -2017-09-20,aapl,se,-1,156.07 -2017-09-21,nflx,sx,1,188.78 -2017-09-21,nflx,le,1,188.78 -2017-09-21,amzn,lx,-1,964.65 -2017-09-21,amzn,se,-1,964.65 -2017-09-22,nflx,lx,-1,187.35 -2017-09-22,nflx,se,-1,187.35 -2017-09-22,googl,lx,-1,943.26 -2017-09-22,googl,se,-1,943.26 -2017-09-26,nflx,sx,1,179.38 -2017-09-26,nflx,le,1,179.38 -2017-09-26,fb,sx,1,164.21 -2017-09-26,fb,le,1,164.21 -2017-09-26,aapl,sx,1,153.14 -2017-09-26,aapl,le,1,153.14 -2017-09-26,googl,sx,1,937.43 -2017-09-26,googl,le,1,937.43 -2017-09-27,amzn,sx,1,950.87 -2017-09-27,amzn,le,1,950.87 -2017-09-28,nflx,lx,-1,180.7 -2017-09-28,nflx,se,-1,180.7 -2017-09-28,aapl,lx,-1,153.28 -2017-09-28,aapl,se,-1,153.28 -2017-09-29,nflx,sx,1,181.35 -2017-09-29,nflx,le,1,181.35 -2017-09-29,aapl,sx,1,154.12 -2017-09-29,aapl,le,1,154.12 -2017-10-02,nflx,lx,-1,177.01 -2017-10-02,nflx,se,-1,177.01 -2017-10-02,amzn,lx,-1,959.19 -2017-10-02,amzn,se,-1,959.19 -2017-10-02,fb,lx,-1,169.47 -2017-10-02,fb,se,-1,169.47 -2017-10-02,aapl,lx,-1,153.81 -2017-10-02,aapl,se,-1,153.81 -2017-10-02,googl,lx,-1,967.47 -2017-10-02,googl,se,-1,967.47 -2017-10-03,nflx,sx,1,179.19 -2017-10-03,nflx,le,1,179.19 -2017-10-03,fb,sx,1,169.96 -2017-10-03,fb,le,1,169.96 -2017-10-03,aapl,sx,1,154.48 -2017-10-03,aapl,le,1,154.48 -2017-10-03,googl,sx,1,972.08 -2017-10-03,googl,le,1,972.08 -2017-10-04,amzn,sx,1,965.45 -2017-10-04,amzn,le,1,965.45 -2017-10-04,fb,lx,-1,168.42 -2017-10-04,fb,se,-1,168.42 -2017-10-04,aapl,lx,-1,153.4508 -2017-10-04,aapl,se,-1,153.4508 -2017-10-04,googl,lx,-1,966.78 -2017-10-04,googl,se,-1,966.78 -2017-10-05,fb,sx,1,171.24 -2017-10-05,fb,le,1,171.24 -2017-10-05,aapl,sx,1,155.39 -2017-10-05,aapl,le,1,155.39 -2017-10-05,googl,sx,1,985.19 -2017-10-05,googl,le,1,985.19 -2017-10-06,aapl,lx,-1,155.3 -2017-10-06,aapl,se,-1,155.3 -2017-10-09,nflx,lx,-1,196.87 -2017-10-09,nflx,se,-1,196.87 -2017-10-09,aapl,sx,1,155.84 -2017-10-09,aapl,le,1,155.84 -2017-10-09,googl,lx,-1,992.31 -2017-10-09,googl,se,-1,992.31 -2017-10-10,amzn,lx,-1,987.2 -2017-10-10,amzn,se,-1,987.2 -2017-10-10,fb,lx,-1,171.59 -2017-10-10,fb,se,-1,171.59 -2017-10-11,amzn,sx,1,995.0 -2017-10-11,amzn,le,1,995.0 -2017-10-11,fb,sx,1,172.74 -2017-10-11,fb,le,1,172.74 -2017-10-11,googl,sx,1,1005.65 -2017-10-11,googl,le,1,1005.65 -2017-10-12,nflx,sx,1,195.86 -2017-10-12,nflx,le,1,195.86 -2017-10-12,fb,lx,-1,172.55 -2017-10-12,fb,se,-1,172.55 -2017-10-12,aapl,lx,-1,156.0 -2017-10-12,aapl,se,-1,156.0 -2017-10-13,fb,sx,1,173.74 -2017-10-13,fb,le,1,173.74 -2017-10-13,aapl,sx,1,156.99 -2017-10-13,aapl,le,1,156.99 -2017-10-17,nflx,lx,-1,199.48 -2017-10-17,nflx,se,-1,199.48 -2017-10-18,amzn,lx,-1,997.0 -2017-10-18,amzn,se,-1,997.0 -2017-10-18,fb,lx,-1,176.03 -2017-10-18,fb,se,-1,176.03 -2017-10-18,aapl,lx,-1,159.76 -2017-10-18,aapl,se,-1,159.76 -2017-10-19,googl,lx,-1,1001.84 -2017-10-19,googl,se,-1,1001.84 -2017-10-20,fb,sx,1,174.98 -2017-10-20,fb,le,1,174.98 -2017-10-20,aapl,sx,1,156.16 -2017-10-20,aapl,le,1,156.16 -2017-10-20,googl,sx,1,1005.07 -2017-10-20,googl,le,1,1005.07 -2017-10-23,fb,lx,-1,171.27 -2017-10-23,fb,se,-1,171.27 -2017-10-23,googl,lx,-1,985.54 -2017-10-23,googl,se,-1,985.54 -2017-10-24,nflx,sx,1,196.02 -2017-10-24,nflx,le,1,196.02 -2017-10-24,amzn,sx,1,975.9 -2017-10-24,amzn,le,1,975.9 -2017-10-24,fb,sx,1,171.8 -2017-10-24,fb,le,1,171.8 -2017-10-24,googl,sx,1,988.49 -2017-10-24,googl,le,1,988.49 -2017-10-25,nflx,lx,-1,193.77 -2017-10-25,nflx,se,-1,193.77 -2017-10-25,amzn,lx,-1,972.91 -2017-10-25,amzn,se,-1,972.91 -2017-10-25,fb,lx,-1,170.6 -2017-10-25,fb,se,-1,170.6 -2017-10-25,aapl,lx,-1,156.405 -2017-10-25,aapl,se,-1,156.405 -2017-10-26,nflx,sx,1,195.21 -2017-10-26,nflx,le,1,195.21 -2017-10-26,fb,sx,1,170.63 -2017-10-26,fb,le,1,170.63 -2017-10-26,aapl,sx,1,157.41 -2017-10-26,aapl,le,1,157.41 -2017-10-26,googl,lx,-1,991.42 -2017-10-26,googl,se,-1,991.42 -2017-10-27,amzn,sx,1,1100.95 -2017-10-27,amzn,le,1,1100.95 -2017-10-27,googl,sx,1,1033.67 -2017-10-27,googl,le,1,1033.67 -2017-10-30,nflx,lx,-1,198.37 -2017-10-30,nflx,se,-1,198.37 -2017-10-30,googl,lx,-1,1033.13 -2017-10-30,googl,se,-1,1033.13 -2017-10-31,amzn,lx,-1,1105.28 -2017-10-31,amzn,se,-1,1105.28 -2017-11-01,nflx,sx,1,198.0 -2017-11-01,nflx,le,1,198.0 -2017-11-01,aapl,lx,-1,166.89 -2017-11-01,aapl,se,-1,166.89 -2017-11-01,googl,sx,1,1042.6 -2017-11-01,googl,le,1,1042.6 -2017-11-02,fb,lx,-1,178.92 -2017-11-02,fb,se,-1,178.92 -2017-11-02,aapl,sx,1,168.11 -2017-11-02,aapl,le,1,168.11 -2017-11-03,amzn,sx,1,1111.6 -2017-11-03,amzn,le,1,1111.6 -2017-11-06,fb,sx,1,180.17 -2017-11-06,fb,le,1,180.17 -2017-11-06,googl,lx,-1,1042.68 -2017-11-06,googl,se,-1,1042.68 -2017-11-07,nflx,lx,-1,195.89 -2017-11-07,nflx,se,-1,195.89 -2017-11-07,googl,sx,1,1052.39 -2017-11-07,googl,le,1,1052.39 -2017-11-09,fb,lx,-1,179.3 -2017-11-09,fb,se,-1,179.3 -2017-11-09,googl,lx,-1,1047.72 -2017-11-09,googl,se,-1,1047.72 -2017-11-10,amzn,lx,-1,1125.35 -2017-11-10,amzn,se,-1,1125.35 -2017-11-10,aapl,lx,-1,174.67 -2017-11-10,aapl,se,-1,174.67 -2017-11-13,nflx,sx,1,195.08 -2017-11-13,nflx,le,1,195.08 -2017-11-13,amzn,sx,1,1129.17 -2017-11-13,amzn,le,1,1129.17 -2017-11-13,fb,sx,1,178.77 -2017-11-13,fb,le,1,178.77 -2017-11-14,fb,lx,-1,178.07 -2017-11-14,fb,se,-1,178.07 -2017-11-14,googl,sx,1,1041.64 -2017-11-14,googl,le,1,1041.64 -2017-11-15,nflx,lx,-1,192.12 -2017-11-15,nflx,se,-1,192.12 -2017-11-15,amzn,lx,-1,1126.69 -2017-11-15,amzn,se,-1,1126.69 -2017-11-15,googl,lx,-1,1036.41 -2017-11-15,googl,se,-1,1036.41 -2017-11-16,nflx,sx,1,195.51 -2017-11-16,nflx,le,1,195.51 -2017-11-16,amzn,sx,1,1137.29 -2017-11-16,amzn,le,1,1137.29 -2017-11-16,fb,sx,1,179.59 -2017-11-16,fb,le,1,179.59 -2017-11-16,aapl,sx,1,171.1 -2017-11-16,aapl,le,1,171.1 -2017-11-16,googl,sx,1,1048.47 -2017-11-16,googl,le,1,1048.47 -2017-11-17,nflx,lx,-1,193.2 -2017-11-17,nflx,se,-1,193.2 -2017-11-17,amzn,lx,-1,1129.88 -2017-11-17,amzn,se,-1,1129.88 -2017-11-17,fb,lx,-1,179.0 -2017-11-17,fb,se,-1,179.0 -2017-11-17,aapl,lx,-1,170.15 -2017-11-17,aapl,se,-1,170.15 -2017-11-17,googl,lx,-1,1035.89 -2017-11-17,googl,se,-1,1035.89 -2017-11-20,nflx,sx,1,194.1 -2017-11-20,nflx,le,1,194.1 -2017-11-21,amzn,sx,1,1139.49 -2017-11-21,amzn,le,1,1139.49 -2017-11-21,fb,sx,1,181.86 -2017-11-21,fb,le,1,181.86 -2017-11-21,aapl,sx,1,173.14 -2017-11-21,aapl,le,1,173.14 -2017-11-21,googl,sx,1,1050.3 -2017-11-21,googl,le,1,1050.3 -2017-11-22,fb,lx,-1,180.87 -2017-11-22,fb,se,-1,180.87 -2017-11-24,nflx,lx,-1,195.75 -2017-11-24,nflx,se,-1,195.75 -2017-11-24,fb,sx,1,182.78 -2017-11-24,fb,le,1,182.78 -2017-11-27,aapl,lx,-1,174.09 -2017-11-27,aapl,se,-1,174.09 -2017-11-28,nflx,sx,1,199.18 -2017-11-28,nflx,le,1,199.18 -2017-11-28,amzn,lx,-1,1193.6 -2017-11-28,amzn,se,-1,1193.6 -2017-11-28,fb,lx,-1,182.42 -2017-11-28,fb,se,-1,182.42 -2017-11-28,googl,lx,-1,1063.29 -2017-11-28,googl,se,-1,1063.29 -2017-11-29,nflx,lx,-1,188.15 -2017-11-29,nflx,se,-1,188.15 -2017-11-30,amzn,sx,1,1176.75 -2017-11-30,amzn,le,1,1176.75 -2017-11-30,fb,sx,1,177.18 -2017-11-30,fb,le,1,177.18 -2017-11-30,aapl,sx,1,171.85 -2017-11-30,aapl,le,1,171.85 -2017-12-01,amzn,lx,-1,1162.35 -2017-12-01,amzn,se,-1,1162.35 -2017-12-01,fb,lx,-1,175.1 -2017-12-01,fb,se,-1,175.1 -2017-12-01,aapl,lx,-1,171.05 -2017-12-01,aapl,se,-1,171.05 -2017-12-05,nflx,sx,1,184.21 -2017-12-05,nflx,le,1,184.21 -2017-12-05,amzn,sx,1,1141.57 -2017-12-05,amzn,le,1,1141.57 -2017-12-05,fb,sx,1,172.83 -2017-12-05,fb,le,1,172.83 -2017-12-05,googl,sx,1,1019.6 -2017-12-05,googl,le,1,1019.6 -2017-12-07,nflx,lx,-1,185.2 -2017-12-07,nflx,se,-1,185.2 -2017-12-07,aapl,sx,1,169.452 -2017-12-07,aapl,le,1,169.452 -2017-12-08,nflx,sx,1,188.54 -2017-12-08,nflx,le,1,188.54 -2017-12-08,fb,lx,-1,179.0 -2017-12-08,fb,se,-1,179.0 -2017-12-08,aapl,lx,-1,169.37 -2017-12-08,aapl,se,-1,169.37 -2017-12-11,nflx,lx,-1,186.22 -2017-12-11,nflx,se,-1,186.22 -2017-12-11,fb,sx,1,179.04 -2017-12-11,fb,le,1,179.04 -2017-12-11,aapl,sx,1,172.67 -2017-12-11,aapl,le,1,172.67 -2017-12-12,amzn,lx,-1,1165.08 -2017-12-12,amzn,se,-1,1165.08 -2017-12-12,fb,lx,-1,176.96 -2017-12-12,fb,se,-1,176.96 -2017-12-12,aapl,lx,-1,171.7 -2017-12-12,aapl,se,-1,171.7 -2017-12-12,googl,lx,-1,1048.77 -2017-12-12,googl,se,-1,1048.77 -2017-12-13,nflx,sx,1,187.86 -2017-12-13,nflx,le,1,187.86 -2017-12-13,fb,sx,1,178.3 -2017-12-13,fb,le,1,178.3 -2017-12-13,aapl,sx,1,172.27 -2017-12-13,aapl,le,1,172.27 -2017-12-13,googl,sx,1,1051.39 -2017-12-13,googl,le,1,1051.39 -2017-12-14,amzn,sx,1,1174.26 -2017-12-14,amzn,le,1,1174.26 -2017-12-14,aapl,lx,-1,172.22 -2017-12-14,aapl,se,-1,172.22 -2017-12-15,aapl,sx,1,173.87 -2017-12-15,aapl,le,1,173.87 -2017-12-19,nflx,lx,-1,187.02 -2017-12-19,nflx,se,-1,187.02 -2017-12-19,amzn,lx,-1,1187.38 -2017-12-19,amzn,se,-1,1187.38 -2017-12-19,fb,lx,-1,179.51 -2017-12-19,fb,se,-1,179.51 -2017-12-19,aapl,lx,-1,174.54 -2017-12-19,aapl,se,-1,174.54 -2017-12-19,googl,lx,-1,1079.78 -2017-12-19,googl,se,-1,1079.78 -2017-12-20,nflx,sx,1,188.82 -2017-12-20,nflx,le,1,188.82 -2017-12-21,nflx,lx,-1,188.62 -2017-12-21,nflx,se,-1,188.62 -2017-12-21,aapl,sx,1,175.01 -2017-12-21,aapl,le,1,175.01 -2017-12-22,nflx,sx,1,189.94 -2017-12-22,nflx,le,1,189.94 -2017-12-26,nflx,lx,-1,187.76 -2017-12-26,nflx,se,-1,187.76 -2017-12-26,amzn,sx,1,1176.76 -2017-12-26,amzn,le,1,1176.76 -2017-12-26,aapl,lx,-1,170.57 -2017-12-26,aapl,se,-1,170.57 -2017-12-27,fb,sx,1,177.62 -2017-12-27,fb,le,1,177.62 -2017-12-27,aapl,sx,1,170.6 -2017-12-27,aapl,le,1,170.6 -2017-12-28,nflx,sx,1,192.71 -2017-12-28,nflx,le,1,192.71 -2017-12-29,nflx,lx,-1,191.96 -2017-12-29,nflx,se,-1,191.96 -2017-12-29,amzn,lx,-1,1169.47 -2017-12-29,amzn,se,-1,1169.47 -2017-12-29,fb,lx,-1,176.46 -2017-12-29,fb,se,-1,176.46 -2017-12-29,aapl,lx,-1,169.23 -2017-12-29,aapl,se,-1,169.23 diff --git a/alphapy/examples/Trading System/systems/faang_closer_transactions_1d.csv b/alphapy/examples/Trading System/systems/faang_closer_transactions_1d.csv deleted file mode 100644 index f501315..0000000 --- a/alphapy/examples/Trading System/systems/faang_closer_transactions_1d.csv +++ /dev/null @@ -1,1748 +0,0 @@ -date,amount,price,symbol -2016-08-19,-208.0,95.87,nflx -2016-08-19,-26.0,757.31,amzn -2016-08-19,-161.0,123.56,fb -2016-08-19,182.0,109.36,aapl -2016-08-19,-25.0,799.65,googl -2016-08-22,26.0,759.48,amzn -2016-08-22,26.0,759.48,amzn -2016-08-22,161.0,124.15,fb -2016-08-22,161.0,124.15,fb -2016-08-22,-182.0,108.51,aapl -2016-08-22,-184.0,108.51,aapl -2016-08-23,208.0,95.94,nflx -2016-08-23,208.0,95.94,nflx -2016-08-23,184.0,108.85,aapl -2016-08-23,183.0,108.85,aapl -2016-08-24,-208.0,95.18,nflx -2016-08-24,-210.0,95.18,nflx -2016-08-24,-26.0,757.25,amzn -2016-08-24,-26.0,757.25,amzn -2016-08-24,-161.0,123.48,fb -2016-08-24,-162.0,123.48,fb -2016-08-24,-183.0,108.03,aapl -2016-08-24,-185.0,108.03,aapl -2016-08-25,210.0,97.32,nflx -2016-08-25,204.0,97.32,nflx -2016-08-25,26.0,759.22,amzn -2016-08-25,26.0,759.22,amzn -2016-08-25,162.0,123.89,fb -2016-08-25,160.0,123.89,fb -2016-08-26,25.0,793.22,googl -2016-08-26,25.0,793.22,googl -2016-08-29,-204.0,97.3,nflx -2016-08-29,-206.0,97.3,nflx -2016-08-30,206.0,97.45,nflx -2016-08-30,206.0,97.45,nflx -2016-08-30,-26.0,767.58,amzn -2016-08-30,-26.0,767.58,amzn -2016-08-30,-160.0,125.84,fb -2016-08-30,-159.0,125.84,fb -2016-08-30,-25.0,791.92,googl -2016-08-30,-25.0,791.92,googl -2016-08-31,26.0,769.16,amzn -2016-08-31,26.0,769.16,amzn -2016-08-31,159.0,126.12,fb -2016-08-31,158.0,126.12,fb -2016-08-31,185.0,106.1,aapl -2016-08-31,188.0,106.1,aapl -2016-09-01,-206.0,97.38,nflx -2016-09-01,-205.0,97.38,nflx -2016-09-01,25.0,791.4,googl -2016-09-01,25.0,791.4,googl -2016-09-06,205.0,100.09,nflx -2016-09-06,201.0,100.09,nflx -2016-09-06,-188.0,107.7,aapl -2016-09-06,-187.0,107.7,aapl -2016-09-07,-201.0,99.15,nflx -2016-09-07,-206.0,99.15,nflx -2016-09-07,-26.0,784.48,amzn -2016-09-07,-26.0,784.48,amzn -2016-09-07,187.0,108.36,aapl -2016-09-07,189.0,108.36,aapl -2016-09-07,-25.0,807.99,googl -2016-09-07,-25.0,807.99,googl -2016-09-08,206.0,99.66,nflx -2016-09-08,205.0,99.66,nflx -2016-09-08,-158.0,130.27,fb -2016-09-08,-157.0,130.27,fb -2016-09-08,-189.0,105.52,aapl -2016-09-08,-194.0,105.52,aapl -2016-09-09,-205.0,96.5,nflx -2016-09-09,-211.0,96.5,nflx -2016-09-12,211.0,99.05,nflx -2016-09-12,200.0,99.05,nflx -2016-09-12,26.0,771.49,amzn -2016-09-12,25.0,771.49,amzn -2016-09-12,157.0,128.69,fb -2016-09-12,154.0,128.69,fb -2016-09-12,194.0,105.44,aapl -2016-09-12,188.0,105.44,aapl -2016-09-12,25.0,798.82,googl -2016-09-12,24.0,798.82,googl -2016-09-13,-200.0,96.09,nflx -2016-09-13,-210.0,96.09,nflx -2016-09-13,-25.0,761.01,amzn -2016-09-13,-26.0,761.01,amzn -2016-09-13,-154.0,127.21,fb -2016-09-13,-158.0,127.21,fb -2016-09-13,-24.0,788.72,googl -2016-09-13,-25.0,788.72,googl -2016-09-14,210.0,97.01,nflx -2016-09-14,206.0,97.01,nflx -2016-09-14,26.0,761.09,amzn -2016-09-14,26.0,761.09,amzn -2016-09-14,158.0,127.77,fb -2016-09-14,156.0,127.77,fb -2016-09-14,25.0,790.46,googl -2016-09-14,25.0,790.46,googl -2016-09-16,-188.0,114.92,aapl -2016-09-16,-178.0,114.92,aapl -2016-09-16,-25.0,797.97,googl -2016-09-16,-25.0,797.97,googl -2016-09-19,-206.0,98.06,nflx -2016-09-19,-210.0,98.06,nflx -2016-09-19,-26.0,775.1,amzn -2016-09-19,-26.0,775.1,amzn -2016-09-19,-156.0,128.65,fb -2016-09-19,-160.0,128.65,fb -2016-09-20,210.0,98.25,nflx -2016-09-20,208.0,98.25,nflx -2016-09-20,26.0,780.22,amzn -2016-09-20,26.0,780.22,amzn -2016-09-20,25.0,799.78,googl -2016-09-20,25.0,799.78,googl -2016-09-21,-208.0,94.88,nflx -2016-09-21,-216.0,94.88,nflx -2016-09-21,160.0,129.94,fb -2016-09-21,158.0,129.94,fb -2016-09-22,216.0,95.83,nflx -2016-09-22,214.0,95.83,nflx -2016-09-22,178.0,114.62,aapl -2016-09-22,179.0,114.62,aapl -2016-09-23,-158.0,127.96,fb -2016-09-23,-162.0,127.96,fb -2016-09-23,-179.0,112.71,aapl -2016-09-23,-184.0,112.71,aapl -2016-09-23,-25.0,814.96,googl -2016-09-23,-25.0,814.96,googl -2016-09-26,-214.0,94.56,nflx -2016-09-26,-218.0,94.56,nflx -2016-09-26,-26.0,799.16,amzn -2016-09-26,-25.0,799.16,amzn -2016-09-26,184.0,112.88,aapl -2016-09-26,182.0,112.88,aapl -2016-09-27,218.0,97.07,nflx -2016-09-27,210.0,97.07,nflx -2016-09-27,25.0,816.11,amzn -2016-09-27,25.0,816.11,amzn -2016-09-27,162.0,128.69,fb -2016-09-27,158.0,128.69,fb -2016-09-27,25.0,810.73,googl -2016-09-27,25.0,810.73,googl -2016-09-28,-25.0,810.06,googl -2016-09-28,-25.0,810.06,googl -2016-09-29,-210.0,96.67,nflx -2016-09-29,-215.0,96.67,nflx -2016-09-29,-158.0,128.09,fb -2016-09-29,-162.0,128.09,fb -2016-09-29,-182.0,112.18,aapl -2016-09-29,-185.0,112.18,aapl -2016-09-30,215.0,98.55,nflx -2016-09-30,209.0,98.55,nflx -2016-09-30,162.0,128.27,fb -2016-09-30,161.0,128.27,fb -2016-09-30,185.0,113.05,aapl -2016-09-30,183.0,113.05,aapl -2016-09-30,25.0,804.06,googl -2016-09-30,25.0,804.06,googl -2016-10-03,-25.0,836.74,amzn -2016-10-03,-24.0,836.74,amzn -2016-10-03,-183.0,112.52,aapl -2016-10-03,-185.0,112.52,aapl -2016-10-03,-25.0,800.38,googl -2016-10-03,-26.0,800.38,googl -2016-10-04,-209.0,102.34,nflx -2016-10-04,-205.0,102.34,nflx -2016-10-04,-161.0,128.19,fb -2016-10-04,-163.0,128.19,fb -2016-10-04,185.0,113.0,aapl -2016-10-04,185.0,113.0,aapl -2016-10-04,26.0,802.79,googl -2016-10-04,26.0,802.79,googl -2016-10-05,205.0,106.28,nflx -2016-10-05,197.0,106.28,nflx -2016-10-05,24.0,844.36,amzn -2016-10-05,24.0,844.36,amzn -2016-10-05,163.0,128.47,fb -2016-10-05,163.0,128.47,fb -2016-10-05,-26.0,801.23,googl -2016-10-05,-26.0,801.23,googl -2016-10-06,-197.0,105.07,nflx -2016-10-06,-201.0,105.07,nflx -2016-10-06,-24.0,841.66,amzn -2016-10-06,-25.0,841.66,amzn -2016-10-06,26.0,803.08,googl -2016-10-06,26.0,803.08,googl -2016-10-07,-26.0,800.71,googl -2016-10-07,-26.0,800.71,googl -2016-10-10,25.0,841.71,amzn -2016-10-10,25.0,841.71,amzn -2016-10-10,26.0,814.17,googl -2016-10-10,26.0,814.17,googl -2016-10-11,-25.0,831.0,amzn -2016-10-11,-25.0,831.0,amzn -2016-10-11,-163.0,128.88,fb -2016-10-11,-165.0,128.88,fb -2016-10-11,-26.0,809.57,googl -2016-10-11,-26.0,809.57,googl -2016-10-12,25.0,834.09,amzn -2016-10-12,25.0,834.09,amzn -2016-10-12,165.0,129.05,fb -2016-10-12,163.0,129.05,fb -2016-10-12,26.0,811.77,googl -2016-10-12,25.0,811.77,googl -2016-10-13,201.0,100.23,nflx -2016-10-13,210.0,100.23,nflx -2016-10-13,-25.0,829.28,amzn -2016-10-13,-25.0,829.28,amzn -2016-10-13,-163.0,127.82,fb -2016-10-13,-165.0,127.82,fb -2016-10-13,-185.0,116.98,aapl -2016-10-13,-180.0,116.98,aapl -2016-10-13,-25.0,804.08,googl -2016-10-13,-26.0,804.08,googl -2016-10-14,165.0,127.88,fb -2016-10-14,164.0,127.88,fb -2016-10-14,180.0,117.63,aapl -2016-10-14,178.0,117.63,aapl -2016-10-14,26.0,804.6,googl -2016-10-14,26.0,804.6,googl -2016-10-17,-210.0,99.8,nflx -2016-10-17,-211.0,99.8,nflx -2016-10-17,-164.0,127.54,fb -2016-10-17,-165.0,127.54,fb -2016-10-17,-178.0,117.55,aapl -2016-10-17,-179.0,117.55,aapl -2016-10-18,211.0,118.79,nflx -2016-10-18,176.0,118.79,nflx -2016-10-18,25.0,817.65,amzn -2016-10-18,25.0,817.65,amzn -2016-10-18,165.0,128.57,fb -2016-10-18,162.0,128.57,fb -2016-10-20,-25.0,810.32,amzn -2016-10-20,-27.0,810.32,amzn -2016-10-20,-162.0,130.0,fb -2016-10-20,-169.0,130.0,fb -2016-10-20,-26.0,821.63,googl -2016-10-20,-26.0,821.63,googl -2016-10-21,27.0,818.99,amzn -2016-10-21,26.0,818.99,amzn -2016-10-21,169.0,132.07,fb -2016-10-21,166.0,132.07,fb -2016-10-21,26.0,824.06,googl -2016-10-21,26.0,824.06,googl -2016-10-24,-176.0,127.33,nflx -2016-10-24,-175.0,127.33,nflx -2016-10-24,179.0,117.65,aapl -2016-10-24,189.0,117.65,aapl -2016-10-25,-26.0,835.18,amzn -2016-10-25,-26.0,835.18,amzn -2016-10-25,-166.0,132.29,fb -2016-10-25,-170.0,132.29,fb -2016-10-25,-26.0,828.55,googl -2016-10-25,-27.0,828.55,googl -2016-10-26,175.0,126.97,nflx -2016-10-26,176.0,126.97,nflx -2016-10-26,-189.0,115.59,aapl -2016-10-26,-194.0,115.59,aapl -2016-10-27,-176.0,126.47,nflx -2016-10-27,-175.0,126.47,nflx -2016-10-28,175.0,126.57,nflx -2016-10-28,174.0,126.57,nflx -2016-10-28,170.0,131.29,fb -2016-10-28,168.0,131.29,fb -2016-10-28,27.0,819.56,googl -2016-10-28,26.0,819.56,googl -2016-10-31,-174.0,124.87,nflx -2016-10-31,-175.0,124.87,nflx -2016-10-31,26.0,789.82,amzn -2016-10-31,27.0,789.82,amzn -2016-10-31,-168.0,130.99,fb -2016-10-31,-167.0,130.99,fb -2016-10-31,-26.0,809.9,googl -2016-10-31,-27.0,809.9,googl -2016-11-01,-27.0,785.41,amzn -2016-11-01,-27.0,785.41,amzn -2016-11-02,194.0,111.59,aapl -2016-11-02,193.0,111.59,aapl -2016-11-03,27.0,767.03,amzn -2016-11-03,27.0,767.03,amzn -2016-11-03,-193.0,109.83,aapl -2016-11-03,-193.0,109.83,aapl -2016-11-04,-27.0,755.05,amzn -2016-11-04,-27.0,755.05,amzn -2016-11-04,167.0,120.75,fb -2016-11-04,173.0,120.75,fb -2016-11-07,175.0,124.58,nflx -2016-11-07,167.0,124.58,nflx -2016-11-07,27.0,784.93,amzn -2016-11-07,26.0,784.93,amzn -2016-11-07,193.0,110.41,aapl -2016-11-07,188.0,110.41,aapl -2016-11-07,27.0,802.03,googl -2016-11-07,26.0,802.03,googl -2016-11-08,-167.0,124.34,nflx -2016-11-08,-171.0,124.34,nflx -2016-11-09,-26.0,771.88,amzn -2016-11-09,-27.0,771.88,amzn -2016-11-09,-173.0,123.18,fb -2016-11-09,-174.0,123.18,fb -2016-11-09,-188.0,110.88,aapl -2016-11-09,-193.0,110.88,aapl -2016-11-09,-26.0,805.59,googl -2016-11-09,-26.0,805.59,googl -2016-11-11,193.0,108.43,aapl -2016-11-11,189.0,108.43,aapl -2016-11-14,-189.0,105.71,aapl -2016-11-14,-193.0,105.71,aapl -2016-11-15,171.0,113.59,nflx -2016-11-15,175.0,113.59,nflx -2016-11-15,27.0,743.24,amzn -2016-11-15,26.0,743.24,amzn -2016-11-15,174.0,117.2,fb -2016-11-15,169.0,117.2,fb -2016-11-15,193.0,107.11,aapl -2016-11-15,185.0,107.11,aapl -2016-11-15,26.0,775.16,googl -2016-11-15,25.0,775.16,googl -2016-11-16,-169.0,116.34,fb -2016-11-16,-174.0,116.34,fb -2016-11-17,-175.0,115.03,nflx -2016-11-17,-177.0,115.03,nflx -2016-11-17,174.0,117.79,fb -2016-11-17,173.0,117.79,fb -2016-11-17,-185.0,109.95,aapl -2016-11-17,-186.0,109.95,aapl -2016-11-18,177.0,115.21,nflx -2016-11-18,178.0,115.21,nflx -2016-11-18,-173.0,117.02,fb -2016-11-18,-176.0,117.02,fb -2016-11-18,186.0,110.06,aapl -2016-11-18,187.0,110.06,aapl -2016-11-18,-25.0,775.97,googl -2016-11-18,-26.0,775.97,googl -2016-11-21,176.0,121.77,fb -2016-11-21,168.0,121.77,fb -2016-11-21,26.0,784.8,googl -2016-11-21,26.0,784.8,googl -2016-11-22,-168.0,121.47,fb -2016-11-22,-173.0,121.47,fb -2016-11-23,-178.0,117.69,nflx -2016-11-23,-178.0,117.69,nflx -2016-11-23,-26.0,780.12,amzn -2016-11-23,-26.0,780.12,amzn -2016-11-23,-187.0,111.23,aapl -2016-11-23,-189.0,111.23,aapl -2016-11-23,-26.0,779.0,googl -2016-11-23,-27.0,779.0,googl -2016-11-25,26.0,780.37,amzn -2016-11-25,26.0,780.37,amzn -2016-11-25,189.0,111.79,aapl -2016-11-25,187.0,111.79,aapl -2016-11-25,27.0,780.23,googl -2016-11-25,26.0,780.23,googl -2016-11-28,-26.0,766.77,amzn -2016-11-28,-27.0,766.77,amzn -2016-11-28,173.0,120.41,fb -2016-11-28,173.0,120.41,fb -2016-11-28,-187.0,111.57,aapl -2016-11-28,-187.0,111.57,aapl -2016-11-29,178.0,117.51,nflx -2016-11-29,177.0,117.51,nflx -2016-11-30,-177.0,117.0,nflx -2016-11-30,-178.0,117.0,nflx -2016-11-30,-173.0,118.42,fb -2016-11-30,-176.0,118.42,fb -2016-11-30,-26.0,775.88,googl -2016-11-30,-26.0,775.88,googl -2016-12-01,178.0,117.22,nflx -2016-12-01,175.0,117.22,nflx -2016-12-02,176.0,115.4,fb -2016-12-02,176.0,115.4,fb -2016-12-02,187.0,109.9,aapl -2016-12-02,185.0,109.9,aapl -2016-12-02,26.0,764.46,googl -2016-12-02,26.0,764.46,googl -2016-12-05,-175.0,119.16,nflx -2016-12-05,-172.0,119.16,nflx -2016-12-05,27.0,759.36,amzn -2016-12-05,27.0,759.36,amzn -2016-12-05,-185.0,109.11,aapl -2016-12-05,-188.0,109.11,aapl -2016-12-06,172.0,124.57,nflx -2016-12-06,165.0,124.57,nflx -2016-12-06,-176.0,117.31,fb -2016-12-06,-176.0,117.31,fb -2016-12-06,188.0,109.95,aapl -2016-12-06,188.0,109.95,aapl -2016-12-06,-26.0,776.18,googl -2016-12-06,-26.0,776.18,googl -2016-12-07,176.0,117.95,fb -2016-12-07,177.0,117.95,fb -2016-12-07,26.0,791.47,googl -2016-12-07,26.0,791.47,googl -2016-12-08,-165.0,123.24,nflx -2016-12-08,-171.0,123.24,nflx -2016-12-08,-27.0,767.33,amzn -2016-12-08,-27.0,767.33,amzn -2016-12-09,27.0,768.66,amzn -2016-12-09,27.0,768.66,amzn -2016-12-12,-27.0,760.12,amzn -2016-12-12,-27.0,760.12,amzn -2016-12-12,-177.0,117.77,fb -2016-12-12,-180.0,117.77,fb -2016-12-12,-188.0,113.3,aapl -2016-12-12,-187.0,113.3,aapl -2016-12-12,-26.0,807.9,googl -2016-12-12,-26.0,807.9,googl -2016-12-13,171.0,123.78,nflx -2016-12-13,170.0,123.78,nflx -2016-12-13,27.0,774.34,amzn -2016-12-13,27.0,774.34,amzn -2016-12-13,180.0,120.31,fb -2016-12-13,175.0,120.31,fb -2016-12-13,187.0,115.19,aapl -2016-12-13,183.0,115.19,aapl -2016-12-13,26.0,815.34,googl -2016-12-13,25.0,815.34,googl -2016-12-14,-170.0,123.44,nflx -2016-12-14,-173.0,123.44,nflx -2016-12-14,-27.0,768.82,amzn -2016-12-14,-27.0,768.82,amzn -2016-12-14,-175.0,120.21,fb -2016-12-14,-178.0,120.21,fb -2016-12-15,173.0,125.0,nflx -2016-12-15,171.0,125.0,nflx -2016-12-15,178.0,120.57,fb -2016-12-15,177.0,120.57,fb -2016-12-15,-25.0,815.65,googl -2016-12-15,-26.0,815.65,googl -2016-12-16,-171.0,124.22,nflx -2016-12-16,-172.0,124.22,nflx -2016-12-16,-177.0,119.87,fb -2016-12-16,-178.0,119.87,fb -2016-12-19,172.0,125.45,nflx -2016-12-19,170.0,125.45,nflx -2016-12-19,27.0,766.0,amzn -2016-12-19,27.0,766.0,amzn -2016-12-19,26.0,812.5,googl -2016-12-19,26.0,812.5,googl -2016-12-20,-170.0,125.12,nflx -2016-12-20,-171.0,125.12,nflx -2016-12-21,171.0,126.5,nflx -2016-12-21,169.0,126.5,nflx -2016-12-21,-27.0,770.6,amzn -2016-12-21,-27.0,770.6,amzn -2016-12-21,-26.0,812.2,googl -2016-12-21,-26.0,812.2,googl -2016-12-22,-169.0,125.58,nflx -2016-12-22,-171.0,125.58,nflx -2016-12-22,-183.0,116.29,aapl -2016-12-22,-185.0,116.29,aapl -2016-12-23,171.0,125.59,nflx -2016-12-23,170.0,125.59,nflx -2016-12-23,185.0,116.52,aapl -2016-12-23,183.0,116.52,aapl -2016-12-27,27.0,771.4,amzn -2016-12-27,27.0,771.4,amzn -2016-12-27,178.0,118.01,fb -2016-12-27,180.0,118.01,fb -2016-12-27,26.0,809.93,googl -2016-12-27,26.0,809.93,googl -2016-12-28,-170.0,125.89,nflx -2016-12-28,-171.0,125.89,nflx -2016-12-28,-180.0,116.92,fb -2016-12-28,-184.0,116.92,fb -2016-12-28,-183.0,116.76,aapl -2016-12-28,-184.0,116.76,aapl -2016-12-28,-26.0,804.57,googl -2016-12-28,-26.0,804.57,googl -2016-12-29,-27.0,765.15,amzn -2016-12-29,-27.0,765.15,amzn -2017-01-03,171.0,127.49,nflx -2017-01-03,164.0,127.49,nflx -2017-01-03,27.0,753.67,amzn -2017-01-03,27.0,753.67,amzn -2017-01-03,184.0,116.86,fb -2017-01-03,179.0,116.86,fb -2017-01-03,184.0,116.15,aapl -2017-01-03,181.0,116.15,aapl -2017-01-03,26.0,808.01,googl -2017-01-03,26.0,808.01,googl -2017-01-04,-181.0,116.02,aapl -2017-01-04,-183.0,116.02,aapl -2017-01-04,-26.0,807.77,googl -2017-01-04,-26.0,807.77,googl -2017-01-05,183.0,116.61,aapl -2017-01-05,184.0,116.61,aapl -2017-01-05,26.0,813.02,googl -2017-01-05,26.0,813.02,googl -2017-01-06,-164.0,131.07,nflx -2017-01-06,-166.0,131.07,nflx -2017-01-10,-27.0,795.9,amzn -2017-01-10,-27.0,795.9,amzn -2017-01-10,-179.0,124.35,fb -2017-01-10,-178.0,124.35,fb -2017-01-10,-26.0,826.01,googl -2017-01-10,-26.0,826.01,googl -2017-01-11,166.0,130.5,nflx -2017-01-11,169.0,130.5,nflx -2017-01-11,27.0,799.02,amzn -2017-01-11,27.0,799.02,amzn -2017-01-11,178.0,126.09,fb -2017-01-11,175.0,126.09,fb -2017-01-11,26.0,829.86,googl -2017-01-11,26.0,829.86,googl -2017-01-12,-169.0,129.18,nflx -2017-01-12,-172.0,129.18,nflx -2017-01-12,-184.0,119.25,aapl -2017-01-12,-186.0,119.25,aapl -2017-01-12,-26.0,829.53,googl -2017-01-12,-26.0,829.53,googl -2017-01-13,172.0,133.7,nflx -2017-01-13,166.0,133.7,nflx -2017-01-13,26.0,830.94,googl -2017-01-13,26.0,830.94,googl -2017-01-17,-166.0,132.89,nflx -2017-01-17,-169.0,132.89,nflx -2017-01-17,-27.0,809.72,amzn -2017-01-17,-27.0,809.72,amzn -2017-01-17,-175.0,127.87,fb -2017-01-17,-176.0,127.87,fb -2017-01-17,186.0,120.0,aapl -2017-01-17,187.0,120.0,aapl -2017-01-17,-26.0,827.46,googl -2017-01-17,-27.0,827.46,googl -2017-01-18,169.0,133.26,nflx -2017-01-18,168.0,133.26,nflx -2017-01-18,176.0,127.92,fb -2017-01-18,175.0,127.92,fb -2017-01-18,-187.0,119.99,aapl -2017-01-18,-187.0,119.99,aapl -2017-01-18,27.0,829.02,googl -2017-01-18,27.0,829.02,googl -2017-01-19,27.0,809.04,amzn -2017-01-19,27.0,809.04,amzn -2017-01-19,-175.0,127.55,fb -2017-01-19,-176.0,127.55,fb -2017-01-19,-27.0,824.37,googl -2017-01-19,-27.0,824.37,googl -2017-01-20,-27.0,808.33,amzn -2017-01-20,-27.0,808.33,amzn -2017-01-20,187.0,120.0,aapl -2017-01-20,188.0,120.0,aapl -2017-01-20,27.0,828.17,googl -2017-01-20,27.0,828.17,googl -2017-01-23,-168.0,137.39,nflx -2017-01-23,-164.0,137.39,nflx -2017-01-23,27.0,817.88,amzn -2017-01-23,27.0,817.88,amzn -2017-01-23,176.0,128.93,fb -2017-01-23,175.0,128.93,fb -2017-01-24,164.0,140.11,nflx -2017-01-24,162.0,140.11,nflx -2017-01-24,-188.0,119.97,aapl -2017-01-24,-189.0,119.97,aapl -2017-01-25,-162.0,139.52,nflx -2017-01-25,-164.0,139.52,nflx -2017-01-25,189.0,121.88,aapl -2017-01-25,188.0,121.88,aapl -2017-01-26,-27.0,856.98,googl -2017-01-26,-27.0,856.98,googl -2017-01-27,164.0,142.45,nflx -2017-01-27,163.0,142.45,nflx -2017-01-27,-27.0,835.77,amzn -2017-01-27,-27.0,835.77,amzn -2017-01-27,-175.0,132.18,fb -2017-01-27,-175.0,132.18,fb -2017-01-30,-163.0,141.22,nflx -2017-01-30,-164.0,141.22,nflx -2017-01-30,-188.0,121.63,aapl -2017-01-30,-191.0,121.63,aapl -2017-02-01,164.0,140.78,nflx -2017-02-01,162.0,140.78,nflx -2017-02-01,27.0,832.35,amzn -2017-02-01,27.0,832.35,amzn -2017-02-01,175.0,133.23,fb -2017-02-01,171.0,133.23,fb -2017-02-01,191.0,128.75,aapl -2017-02-01,177.0,128.75,aapl -2017-02-02,-162.0,139.2,nflx -2017-02-02,-167.0,139.2,nflx -2017-02-02,-171.0,130.84,fb -2017-02-02,-178.0,130.84,fb -2017-02-02,-177.0,128.53,aapl -2017-02-02,-181.0,128.53,aapl -2017-02-02,27.0,818.26,googl -2017-02-02,28.0,818.26,googl -2017-02-03,167.0,140.25,nflx -2017-02-03,165.0,140.25,nflx -2017-02-03,-27.0,810.2,amzn -2017-02-03,-28.0,810.2,amzn -2017-02-03,178.0,130.98,fb -2017-02-03,177.0,130.98,fb -2017-02-03,181.0,129.08,aapl -2017-02-03,179.0,129.08,aapl -2017-02-07,28.0,812.5,amzn -2017-02-07,28.0,812.5,amzn -2017-02-07,-177.0,131.84,fb -2017-02-07,-176.0,131.84,fb -2017-02-08,176.0,134.2,fb -2017-02-08,174.0,134.2,fb -2017-02-09,-165.0,144.14,nflx -2017-02-09,-163.0,144.14,nflx -2017-02-09,-174.0,134.14,fb -2017-02-09,-175.0,134.14,fb -2017-02-10,163.0,144.82,nflx -2017-02-10,162.0,144.82,nflx -2017-02-10,175.0,134.19,fb -2017-02-10,175.0,134.19,fb -2017-02-10,-179.0,132.12,aapl -2017-02-10,-178.0,132.12,aapl -2017-02-13,-162.0,143.2,nflx -2017-02-13,-165.0,143.2,nflx -2017-02-13,-175.0,134.05,fb -2017-02-13,-176.0,134.05,fb -2017-02-13,178.0,133.29,aapl -2017-02-13,177.0,133.29,aapl -2017-02-14,-28.0,836.39,amzn -2017-02-14,-28.0,836.39,amzn -2017-02-15,165.0,142.27,nflx -2017-02-15,166.0,142.27,nflx -2017-02-15,28.0,842.7,amzn -2017-02-15,28.0,842.7,amzn -2017-02-15,-28.0,837.32,googl -2017-02-15,-28.0,837.32,googl -2017-02-16,-166.0,142.01,nflx -2017-02-16,-167.0,142.01,nflx -2017-02-16,176.0,133.84,fb -2017-02-16,177.0,133.84,fb -2017-02-16,-177.0,135.345,aapl -2017-02-16,-175.0,135.345,aapl -2017-02-16,28.0,842.17,googl -2017-02-16,28.0,842.17,googl -2017-02-17,167.0,142.22,nflx -2017-02-17,167.0,142.22,nflx -2017-02-17,-177.0,133.53,fb -2017-02-17,-178.0,133.53,fb -2017-02-17,175.0,135.72,aapl -2017-02-17,175.0,135.72,aapl -2017-02-21,178.0,133.72,fb -2017-02-21,178.0,133.72,fb -2017-02-22,-28.0,855.61,amzn -2017-02-22,-28.0,855.61,amzn -2017-02-23,-167.0,142.78,nflx -2017-02-23,-169.0,142.78,nflx -2017-02-23,-178.0,135.36,fb -2017-02-23,-178.0,135.36,fb -2017-02-23,-175.0,136.53,aapl -2017-02-23,-176.0,136.53,aapl -2017-02-23,-28.0,851.0,googl -2017-02-23,-28.0,851.0,googl -2017-02-24,169.0,143.25,nflx -2017-02-24,167.0,143.25,nflx -2017-02-24,178.0,135.44,fb -2017-02-24,177.0,135.44,fb -2017-02-24,176.0,136.66,aapl -2017-02-24,175.0,136.66,aapl -2017-02-27,28.0,848.64,amzn -2017-02-27,28.0,848.64,amzn -2017-02-27,28.0,849.67,googl -2017-02-27,28.0,849.67,googl -2017-02-28,-167.0,142.13,nflx -2017-02-28,-169.0,142.13,nflx -2017-02-28,-28.0,845.04,amzn -2017-02-28,-28.0,845.04,amzn -2017-02-28,-177.0,135.54,fb -2017-02-28,-177.0,135.54,fb -2017-02-28,-28.0,844.93,googl -2017-02-28,-28.0,844.93,googl -2017-03-01,169.0,142.65,nflx -2017-03-01,167.0,142.65,nflx -2017-03-01,28.0,853.08,amzn -2017-03-01,28.0,853.08,amzn -2017-03-01,177.0,137.42,fb -2017-03-01,174.0,137.42,fb -2017-03-01,28.0,856.75,googl -2017-03-01,27.0,856.75,googl -2017-03-02,-167.0,139.53,nflx -2017-03-02,-173.0,139.53,nflx -2017-03-02,-28.0,848.91,amzn -2017-03-02,-28.0,848.91,amzn -2017-03-02,-174.0,136.76,fb -2017-03-02,-177.0,136.76,fb -2017-03-02,-175.0,138.96,aapl -2017-03-02,-174.0,138.96,aapl -2017-03-02,-27.0,849.85,googl -2017-03-02,-28.0,849.85,googl -2017-03-03,28.0,849.88,amzn -2017-03-03,28.0,849.88,amzn -2017-03-03,177.0,137.17,fb -2017-03-03,175.0,137.17,fb -2017-03-03,174.0,139.78,aapl -2017-03-03,171.0,139.78,aapl -2017-03-06,173.0,141.94,nflx -2017-03-06,169.0,141.94,nflx -2017-03-06,-28.0,846.61,amzn -2017-03-06,-28.0,846.61,amzn -2017-03-06,-171.0,139.34,aapl -2017-03-06,-172.0,139.34,aapl -2017-03-07,-169.0,141.43,nflx -2017-03-07,-170.0,141.43,nflx -2017-03-07,-175.0,137.3,fb -2017-03-07,-175.0,137.3,fb -2017-03-07,172.0,139.52,aapl -2017-03-07,172.0,139.52,aapl -2017-03-07,28.0,851.15,googl -2017-03-07,28.0,851.15,googl -2017-03-08,28.0,850.5,amzn -2017-03-08,28.0,850.5,amzn -2017-03-08,175.0,137.72,fb -2017-03-08,175.0,137.72,fb -2017-03-08,-172.0,139.0,aapl -2017-03-08,-173.0,139.0,aapl -2017-03-09,170.0,140.53,nflx -2017-03-09,171.0,140.53,nflx -2017-03-10,-28.0,852.46,amzn -2017-03-10,-28.0,852.46,amzn -2017-03-10,173.0,139.14,aapl -2017-03-10,173.0,139.14,aapl -2017-03-13,28.0,854.59,amzn -2017-03-13,28.0,854.59,amzn -2017-03-14,-171.0,143.19,nflx -2017-03-14,-170.0,143.19,nflx -2017-03-14,-28.0,852.53,amzn -2017-03-14,-28.0,852.53,amzn -2017-03-14,-175.0,139.32,fb -2017-03-14,-175.0,139.32,fb -2017-03-14,-173.0,138.99,aapl -2017-03-14,-175.0,138.99,aapl -2017-03-15,170.0,145.25,nflx -2017-03-15,167.0,145.25,nflx -2017-03-15,28.0,852.97,amzn -2017-03-15,28.0,852.97,amzn -2017-03-15,175.0,139.72,fb -2017-03-15,174.0,139.72,fb -2017-03-15,175.0,140.46,aapl -2017-03-15,173.0,140.46,aapl -2017-03-16,-167.0,144.39,nflx -2017-03-16,-169.0,144.39,nflx -2017-03-17,169.0,145.11,nflx -2017-03-17,168.0,145.11,nflx -2017-03-17,-28.0,852.31,amzn -2017-03-17,-28.0,852.31,amzn -2017-03-17,-174.0,139.84,fb -2017-03-17,-175.0,139.84,fb -2017-03-17,-173.0,139.99,aapl -2017-03-17,-175.0,139.99,aapl -2017-03-20,28.0,856.97,amzn -2017-03-20,28.0,856.97,amzn -2017-03-20,175.0,139.94,fb -2017-03-20,175.0,139.94,fb -2017-03-20,175.0,141.46,aapl -2017-03-20,173.0,141.46,aapl -2017-03-20,-28.0,867.91,googl -2017-03-20,-28.0,867.91,googl -2017-03-21,-168.0,142.42,nflx -2017-03-21,-172.0,142.42,nflx -2017-03-21,-28.0,843.2,amzn -2017-03-21,-29.0,843.2,amzn -2017-03-21,-175.0,138.51,fb -2017-03-21,-177.0,138.51,fb -2017-03-21,-173.0,139.84,aapl -2017-03-21,-175.0,139.84,aapl -2017-03-22,172.0,142.65,nflx -2017-03-22,169.0,142.65,nflx -2017-03-22,29.0,848.06,amzn -2017-03-22,28.0,848.06,amzn -2017-03-22,177.0,139.59,fb -2017-03-22,173.0,139.59,fb -2017-03-22,175.0,141.42,aapl -2017-03-22,171.0,141.42,aapl -2017-03-23,-169.0,141.84,nflx -2017-03-23,-171.0,141.84,nflx -2017-03-23,-28.0,847.38,amzn -2017-03-23,-28.0,847.38,amzn -2017-03-23,-173.0,139.53,fb -2017-03-23,-174.0,139.53,fb -2017-03-23,-171.0,140.92,aapl -2017-03-23,-172.0,140.92,aapl -2017-03-24,171.0,142.02,nflx -2017-03-24,170.0,142.02,nflx -2017-03-24,174.0,140.34,fb -2017-03-24,172.0,140.34,fb -2017-03-27,28.0,846.82,amzn -2017-03-27,28.0,846.82,amzn -2017-03-27,-172.0,140.32,fb -2017-03-27,-172.0,140.32,fb -2017-03-27,172.0,140.88,aapl -2017-03-27,171.0,140.88,aapl -2017-03-27,28.0,838.51,googl -2017-03-27,28.0,838.51,googl -2017-03-28,172.0,141.76,fb -2017-03-28,171.0,141.76,fb -2017-03-30,-171.0,142.41,fb -2017-03-30,-174.0,142.41,fb -2017-03-30,-171.0,143.93,aapl -2017-03-30,-172.0,143.93,aapl -2017-03-30,-28.0,849.48,googl -2017-03-30,-29.0,849.48,googl -2017-03-31,-170.0,147.81,nflx -2017-03-31,-168.0,147.81,nflx -2017-04-03,174.0,142.28,fb -2017-04-03,174.0,142.28,fb -2017-04-03,172.0,143.7,aapl -2017-04-03,173.0,143.7,aapl -2017-04-03,29.0,856.75,googl -2017-04-03,29.0,856.75,googl -2017-04-04,-174.0,141.73,fb -2017-04-04,-175.0,141.73,fb -2017-04-04,-29.0,852.57,googl -2017-04-04,-29.0,852.57,googl -2017-04-05,175.0,141.85,fb -2017-04-05,175.0,141.85,fb -2017-04-05,-173.0,144.02,aapl -2017-04-05,-173.0,144.02,aapl -2017-04-06,168.0,143.74,nflx -2017-04-06,173.0,143.74,nflx -2017-04-06,-28.0,898.28,amzn -2017-04-06,-27.0,898.28,amzn -2017-04-06,-175.0,141.17,fb -2017-04-06,-176.0,141.17,fb -2017-04-07,-173.0,143.11,nflx -2017-04-07,-172.0,143.11,nflx -2017-04-10,172.0,143.85,nflx -2017-04-10,171.0,143.85,nflx -2017-04-10,27.0,907.04,amzn -2017-04-10,27.0,907.04,amzn -2017-04-10,176.0,141.04,fb -2017-04-10,174.0,141.04,fb -2017-04-11,-27.0,902.36,amzn -2017-04-11,-27.0,902.36,amzn -2017-04-11,-174.0,139.92,fb -2017-04-11,-176.0,139.92,fb -2017-04-12,-171.0,143.83,nflx -2017-04-12,-171.0,143.83,nflx -2017-04-12,173.0,141.8,aapl -2017-04-12,173.0,141.8,aapl -2017-04-12,29.0,841.46,googl -2017-04-12,29.0,841.46,googl -2017-04-13,-173.0,141.05,aapl -2017-04-13,-174.0,141.05,aapl -2017-04-13,-29.0,840.18,googl -2017-04-13,-29.0,840.18,googl -2017-04-17,171.0,147.25,nflx -2017-04-17,166.0,147.25,nflx -2017-04-17,27.0,901.99,amzn -2017-04-17,27.0,901.99,amzn -2017-04-17,176.0,141.42,fb -2017-04-17,173.0,141.42,fb -2017-04-17,174.0,141.83,aapl -2017-04-17,172.0,141.83,aapl -2017-04-17,29.0,855.13,googl -2017-04-17,28.0,855.13,googl -2017-04-18,-166.0,143.36,nflx -2017-04-18,-173.0,143.36,nflx -2017-04-18,-173.0,140.96,fb -2017-04-18,-176.0,140.96,fb -2017-04-18,-172.0,141.2,aapl -2017-04-18,-176.0,141.2,aapl -2017-04-18,-28.0,853.99,googl -2017-04-18,-29.0,853.99,googl -2017-04-19,-27.0,899.2,amzn -2017-04-19,-27.0,899.2,amzn -2017-04-19,176.0,142.27,fb -2017-04-19,173.0,142.27,fb -2017-04-19,29.0,856.51,googl -2017-04-19,28.0,856.51,googl -2017-04-20,173.0,141.18,nflx -2017-04-20,174.0,141.18,nflx -2017-04-20,27.0,902.06,amzn -2017-04-20,27.0,902.06,amzn -2017-04-20,176.0,142.44,aapl -2017-04-20,172.0,142.44,aapl -2017-04-21,-27.0,898.53,amzn -2017-04-21,-27.0,898.53,amzn -2017-04-21,-173.0,143.68,fb -2017-04-21,-172.0,143.68,fb -2017-04-21,-172.0,142.27,aapl -2017-04-21,-174.0,142.27,aapl -2017-04-21,-28.0,858.95,googl -2017-04-21,-28.0,858.95,googl -2017-04-24,27.0,907.41,amzn -2017-04-24,27.0,907.41,amzn -2017-04-24,172.0,145.47,fb -2017-04-24,170.0,145.47,fb -2017-04-24,174.0,143.64,aapl -2017-04-24,173.0,143.64,aapl -2017-04-24,28.0,878.93,googl -2017-04-24,28.0,878.93,googl -2017-04-26,-174.0,150.17,nflx -2017-04-26,-170.0,150.17,nflx -2017-04-26,-173.0,143.6508,aapl -2017-04-26,-177.0,143.6508,aapl -2017-04-27,170.0,153.08,nflx -2017-04-27,166.0,153.08,nflx -2017-04-27,177.0,143.79,aapl -2017-04-27,177.0,143.79,aapl -2017-04-28,-166.0,152.2,nflx -2017-04-28,-168.0,152.2,nflx -2017-04-28,-177.0,143.65,aapl -2017-04-28,-178.0,143.65,aapl -2017-05-01,168.0,155.35,nflx -2017-05-01,167.0,155.35,nflx -2017-05-01,178.0,146.6,aapl -2017-05-01,177.0,146.6,aapl -2017-05-02,-27.0,946.68,amzn -2017-05-02,-27.0,946.68,amzn -2017-05-03,-167.0,155.59,nflx -2017-05-03,-170.0,155.59,nflx -2017-05-03,-170.0,151.8,fb -2017-05-03,-174.0,151.8,fb -2017-05-03,-177.0,147.06,aapl -2017-05-03,-180.0,147.06,aapl -2017-05-04,170.0,157.25,nflx -2017-05-04,168.0,157.25,nflx -2017-05-05,-168.0,156.6,nflx -2017-05-05,-169.0,156.6,nflx -2017-05-05,180.0,148.96,aapl -2017-05-05,177.0,148.96,aapl -2017-05-05,-28.0,950.28,googl -2017-05-05,-27.0,950.28,googl -2017-05-08,27.0,949.04,amzn -2017-05-08,27.0,949.04,amzn -2017-05-08,174.0,151.06,fb -2017-05-08,175.0,151.06,fb -2017-05-08,27.0,958.69,googl -2017-05-08,27.0,958.69,googl -2017-05-09,169.0,157.46,nflx -2017-05-09,170.0,157.46,nflx -2017-05-09,-175.0,150.48,fb -2017-05-09,-177.0,150.48,fb -2017-05-09,-27.0,956.71,googl -2017-05-09,-27.0,956.71,googl -2017-05-10,-27.0,948.95,amzn -2017-05-10,-28.0,948.95,amzn -2017-05-10,-177.0,153.26,aapl -2017-05-10,-175.0,153.26,aapl -2017-05-11,-170.0,158.54,nflx -2017-05-11,-169.0,158.54,nflx -2017-05-11,175.0,153.95,aapl -2017-05-11,174.0,153.95,aapl -2017-05-11,27.0,955.89,googl -2017-05-11,28.0,955.89,googl -2017-05-12,169.0,160.81,nflx -2017-05-12,166.0,160.81,nflx -2017-05-12,28.0,961.35,amzn -2017-05-12,27.0,961.35,amzn -2017-05-12,177.0,150.33,fb -2017-05-12,178.0,150.33,fb -2017-05-12,-28.0,955.14,googl -2017-05-12,-28.0,955.14,googl -2017-05-15,-166.0,160.02,nflx -2017-05-15,-169.0,160.02,nflx -2017-05-15,-27.0,957.97,amzn -2017-05-15,-28.0,957.97,amzn -2017-05-15,-178.0,150.19,fb -2017-05-15,-180.0,150.19,fb -2017-05-15,-174.0,155.7,aapl -2017-05-15,-173.0,155.7,aapl -2017-05-15,28.0,959.22,googl -2017-05-15,28.0,959.22,googl -2017-05-16,28.0,966.07,amzn -2017-05-16,27.0,966.07,amzn -2017-05-17,-27.0,944.52,amzn -2017-05-17,-28.0,944.52,amzn -2017-05-17,-28.0,942.17,googl -2017-05-17,-28.0,942.17,googl -2017-05-18,169.0,155.7,nflx -2017-05-18,168.0,155.7,nflx -2017-05-18,28.0,958.49,amzn -2017-05-18,27.0,958.49,amzn -2017-05-18,180.0,147.66,fb -2017-05-18,177.0,147.66,fb -2017-05-18,173.0,152.54,aapl -2017-05-18,172.0,152.54,aapl -2017-05-18,28.0,950.5,googl -2017-05-18,27.0,950.5,googl -2017-05-23,-177.0,148.07,fb -2017-05-23,-181.0,148.07,fb -2017-05-23,-172.0,153.8,aapl -2017-05-23,-174.0,153.8,aapl -2017-05-24,-168.0,157.75,nflx -2017-05-24,-170.0,157.75,nflx -2017-05-24,181.0,150.04,fb -2017-05-24,179.0,150.04,fb -2017-05-25,170.0,163.05,nflx -2017-05-25,166.0,163.05,nflx -2017-05-25,174.0,153.87,aapl -2017-05-25,175.0,153.87,aapl -2017-05-26,-166.0,162.43,nflx -2017-05-26,-169.0,162.43,nflx -2017-05-26,-175.0,153.61,aapl -2017-05-26,-178.0,153.61,aapl -2017-05-30,169.0,163.22,nflx -2017-05-30,168.0,163.22,nflx -2017-05-30,178.0,153.67,aapl -2017-05-30,178.0,153.67,aapl -2017-05-31,-168.0,163.07,nflx -2017-05-31,-168.0,163.07,nflx -2017-05-31,-27.0,994.62,amzn -2017-05-31,-27.0,994.62,amzn -2017-05-31,-179.0,151.46,fb -2017-05-31,-181.0,151.46,fb -2017-05-31,-178.0,152.76,aapl -2017-05-31,-180.0,152.76,aapl -2017-05-31,-27.0,987.09,googl -2017-05-31,-27.0,987.09,googl -2017-06-01,27.0,995.95,amzn -2017-06-01,27.0,995.95,amzn -2017-06-01,181.0,151.53,fb -2017-06-01,180.0,151.53,fb -2017-06-01,180.0,153.18,aapl -2017-06-01,178.0,153.18,aapl -2017-06-01,27.0,988.29,googl -2017-06-01,27.0,988.29,googl -2017-06-02,168.0,165.18,nflx -2017-06-02,166.0,165.18,nflx -2017-06-05,-166.0,165.06,nflx -2017-06-05,-168.0,165.06,nflx -2017-06-05,-178.0,153.93,aapl -2017-06-05,-180.0,153.93,aapl -2017-06-06,168.0,165.17,nflx -2017-06-06,168.0,165.17,nflx -2017-06-06,-27.0,1002.97,amzn -2017-06-06,-27.0,1002.97,amzn -2017-06-06,-180.0,152.81,fb -2017-06-06,-181.0,152.81,fb -2017-06-06,180.0,154.45,aapl -2017-06-06,179.0,154.45,aapl -2017-06-06,-27.0,996.68,googl -2017-06-06,-27.0,996.68,googl -2017-06-07,27.0,1010.07,amzn -2017-06-07,27.0,1010.07,amzn -2017-06-07,181.0,153.12,fb -2017-06-07,180.0,153.12,fb -2017-06-07,27.0,1001.59,googl -2017-06-07,27.0,1001.59,googl -2017-06-08,-27.0,1009.94,amzn -2017-06-08,-27.0,1009.94,amzn -2017-06-08,-179.0,154.99,aapl -2017-06-08,-179.0,154.99,aapl -2017-06-09,-168.0,158.03,nflx -2017-06-09,-176.0,158.03,nflx -2017-06-09,-180.0,149.63,fb -2017-06-09,-186.0,149.63,fb -2017-06-09,-27.0,970.12,googl -2017-06-09,-28.0,970.12,googl -2017-06-13,176.0,152.72,nflx -2017-06-13,172.0,152.72,nflx -2017-06-13,27.0,980.79,amzn -2017-06-13,26.0,980.79,amzn -2017-06-13,186.0,150.68,fb -2017-06-13,174.0,150.68,fb -2017-06-13,179.0,146.59,aapl -2017-06-13,179.0,146.59,aapl -2017-06-13,28.0,970.5,googl -2017-06-13,27.0,970.5,googl -2017-06-14,-172.0,152.2,nflx -2017-06-14,-175.0,152.2,nflx -2017-06-14,-26.0,976.47,amzn -2017-06-14,-27.0,976.47,amzn -2017-06-14,-174.0,150.25,fb -2017-06-14,-177.0,150.25,fb -2017-06-14,-179.0,145.16,aapl -2017-06-14,-183.0,145.16,aapl -2017-06-14,-27.0,967.93,googl -2017-06-14,-27.0,967.93,googl -2017-06-16,175.0,152.38,nflx -2017-06-16,172.0,152.38,nflx -2017-06-16,27.0,987.71,amzn -2017-06-16,26.0,987.71,amzn -2017-06-16,177.0,150.64,fb -2017-06-16,174.0,150.64,fb -2017-06-19,183.0,146.34,aapl -2017-06-19,180.0,146.34,aapl -2017-06-19,27.0,975.22,googl -2017-06-19,27.0,975.22,googl -2017-06-20,-172.0,152.05,nflx -2017-06-20,-176.0,152.05,nflx -2017-06-20,-26.0,992.59,amzn -2017-06-20,-27.0,992.59,amzn -2017-06-20,-174.0,152.25,fb -2017-06-20,-176.0,152.25,fb -2017-06-20,-180.0,145.01,aapl -2017-06-20,-185.0,145.01,aapl -2017-06-20,-27.0,968.99,googl -2017-06-20,-27.0,968.99,googl -2017-06-21,176.0,155.03,nflx -2017-06-21,172.0,155.03,nflx -2017-06-21,27.0,1002.23,amzn -2017-06-21,26.0,1002.23,amzn -2017-06-21,176.0,153.91,fb -2017-06-21,173.0,153.91,fb -2017-06-21,185.0,145.87,aapl -2017-06-21,182.0,145.87,aapl -2017-06-21,27.0,978.59,googl -2017-06-21,27.0,978.59,googl -2017-06-22,-172.0,154.89,nflx -2017-06-22,-174.0,154.89,nflx -2017-06-22,-26.0,1001.3,amzn -2017-06-22,-26.0,1001.3,amzn -2017-06-22,-173.0,153.4,fb -2017-06-22,-175.0,153.4,fb -2017-06-22,-182.0,145.63,aapl -2017-06-22,-185.0,145.63,aapl -2017-06-22,-27.0,976.62,googl -2017-06-22,-27.0,976.62,googl -2017-06-23,174.0,158.03,nflx -2017-06-23,170.0,158.03,nflx -2017-06-23,26.0,1003.74,amzn -2017-06-23,26.0,1003.74,amzn -2017-06-23,175.0,155.07,fb -2017-06-23,173.0,155.07,fb -2017-06-23,185.0,146.35,aapl -2017-06-23,184.0,146.35,aapl -2017-06-23,27.0,986.09,googl -2017-06-23,27.0,986.09,googl -2017-06-26,-170.0,157.5,nflx -2017-06-26,-172.0,157.5,nflx -2017-06-26,-26.0,993.98,amzn -2017-06-26,-27.0,993.98,amzn -2017-06-26,-173.0,153.59,fb -2017-06-26,-177.0,153.59,fb -2017-06-26,-184.0,145.82,aapl -2017-06-26,-186.0,145.82,aapl -2017-06-26,-27.0,972.09,googl -2017-06-26,-27.0,972.09,googl -2017-06-28,172.0,153.41,nflx -2017-06-28,171.0,153.41,nflx -2017-06-28,27.0,990.33,amzn -2017-06-28,26.0,990.33,amzn -2017-06-28,177.0,153.24,fb -2017-06-28,171.0,153.24,fb -2017-06-28,186.0,145.83,aapl -2017-06-28,180.0,145.83,aapl -2017-06-28,27.0,961.01,googl -2017-06-28,27.0,961.01,googl -2017-06-29,-171.0,150.09,nflx -2017-06-29,-178.0,150.09,nflx -2017-06-29,-26.0,975.93,amzn -2017-06-29,-27.0,975.93,amzn -2017-06-29,-171.0,151.04,fb -2017-06-29,-177.0,151.04,fb -2017-06-29,-180.0,143.68,aapl -2017-06-29,-186.0,143.68,aapl -2017-06-29,-27.0,937.82,googl -2017-06-29,-28.0,937.82,googl -2017-06-30,186.0,144.02,aapl -2017-06-30,182.0,144.02,aapl -2017-07-03,-182.0,143.5,aapl -2017-07-03,-182.0,143.5,aapl -2017-07-05,178.0,147.61,nflx -2017-07-05,174.0,147.61,nflx -2017-07-05,27.0,971.4,amzn -2017-07-05,26.0,971.4,amzn -2017-07-05,177.0,150.34,fb -2017-07-05,171.0,150.34,fb -2017-07-05,182.0,144.09,aapl -2017-07-05,179.0,144.09,aapl -2017-07-05,28.0,932.26,googl -2017-07-05,27.0,932.26,googl -2017-07-06,-174.0,146.25,nflx -2017-07-06,-178.0,146.25,nflx -2017-07-06,-26.0,965.14,amzn -2017-07-06,-27.0,965.14,amzn -2017-07-06,-171.0,148.82,fb -2017-07-06,-175.0,148.82,fb -2017-07-06,-179.0,142.73,aapl -2017-07-06,-183.0,142.73,aapl -2017-07-06,-27.0,927.69,googl -2017-07-06,-28.0,927.69,googl -2017-07-07,178.0,150.18,nflx -2017-07-07,172.0,150.18,nflx -2017-07-07,27.0,978.76,amzn -2017-07-07,26.0,978.76,amzn -2017-07-07,175.0,151.44,fb -2017-07-07,171.0,151.44,fb -2017-07-07,183.0,144.18,aapl -2017-07-07,179.0,144.18,aapl -2017-07-07,28.0,940.81,googl -2017-07-07,27.0,940.81,googl -2017-07-11,-26.0,994.13,amzn -2017-07-11,-26.0,994.13,amzn -2017-07-12,26.0,1006.51,amzn -2017-07-12,26.0,1006.51,amzn -2017-07-13,-172.0,158.17,nflx -2017-07-13,-172.0,158.17,nflx -2017-07-13,-26.0,999.855,amzn -2017-07-13,-27.0,999.855,amzn -2017-07-14,172.0,161.12,nflx -2017-07-14,169.0,161.12,nflx -2017-07-14,27.0,1001.81,amzn -2017-07-14,27.0,1001.81,amzn -2017-07-17,-171.0,159.73,fb -2017-07-17,-172.0,159.73,fb -2017-07-17,-27.0,975.96,googl -2017-07-17,-28.0,975.96,googl -2017-07-18,172.0,162.86,fb -2017-07-18,169.0,162.86,fb -2017-07-18,28.0,986.95,googl -2017-07-18,27.0,986.95,googl -2017-07-20,-169.0,183.6,nflx -2017-07-20,-156.0,183.6,nflx -2017-07-20,-179.0,150.34,aapl -2017-07-20,-190.0,150.34,aapl -2017-07-20,-27.0,992.19,googl -2017-07-20,-28.0,992.19,googl -2017-07-21,156.0,188.54,nflx -2017-07-21,152.0,188.54,nflx -2017-07-21,-27.0,1025.67,amzn -2017-07-21,-27.0,1025.67,amzn -2017-07-21,-169.0,164.43,fb -2017-07-21,-174.0,164.43,fb -2017-07-21,28.0,993.84,googl -2017-07-21,28.0,993.84,googl -2017-07-24,-152.0,187.91,nflx -2017-07-24,-153.0,187.91,nflx -2017-07-24,27.0,1038.95,amzn -2017-07-24,27.0,1038.95,amzn -2017-07-24,174.0,166.0,fb -2017-07-24,173.0,166.0,fb -2017-07-24,190.0,152.09,aapl -2017-07-24,189.0,152.09,aapl -2017-07-25,-173.0,165.28,fb -2017-07-25,-175.0,165.28,fb -2017-07-25,-28.0,969.03,googl -2017-07-25,-29.0,969.03,googl -2017-07-26,153.0,189.08,nflx -2017-07-26,152.0,189.08,nflx -2017-07-26,175.0,165.61,fb -2017-07-26,174.0,165.61,fb -2017-07-27,-152.0,182.68,nflx -2017-07-27,-158.0,182.68,nflx -2017-07-27,-27.0,1046.0,amzn -2017-07-27,-27.0,1046.0,amzn -2017-07-27,-189.0,150.56,aapl -2017-07-27,-192.0,150.56,aapl -2017-07-28,158.0,184.04,nflx -2017-07-28,156.0,184.04,nflx -2017-07-28,29.0,958.33,googl -2017-07-28,30.0,958.33,googl -2017-07-31,-156.0,181.66,nflx -2017-07-31,-158.0,181.66,nflx -2017-07-31,-174.0,169.25,fb -2017-07-31,-169.0,169.25,fb -2017-07-31,-30.0,945.5,googl -2017-07-31,-30.0,945.5,googl -2017-08-01,158.0,182.03,nflx -2017-08-01,155.0,182.03,nflx -2017-08-01,27.0,996.19,amzn -2017-08-01,28.0,996.19,amzn -2017-08-01,169.0,169.86,fb -2017-08-01,166.0,169.86,fb -2017-08-01,192.0,150.05,aapl -2017-08-01,188.0,150.05,aapl -2017-08-01,30.0,946.56,googl -2017-08-01,29.0,946.56,googl -2017-08-02,-155.0,180.74,nflx -2017-08-02,-157.0,180.74,nflx -2017-08-02,-28.0,995.89,amzn -2017-08-02,-28.0,995.89,amzn -2017-08-02,-166.0,169.25,fb -2017-08-02,-167.0,169.25,fb -2017-08-03,-188.0,155.57,aapl -2017-08-03,-183.0,155.57,aapl -2017-08-03,-29.0,940.3,googl -2017-08-03,-30.0,940.3,googl -2017-08-04,157.0,180.27,nflx -2017-08-04,157.0,180.27,nflx -2017-08-04,28.0,987.58,amzn -2017-08-04,28.0,987.58,amzn -2017-08-04,167.0,169.62,fb -2017-08-04,167.0,169.62,fb -2017-08-04,183.0,156.39,aapl -2017-08-04,181.0,156.39,aapl -2017-08-04,30.0,945.79,googl -2017-08-04,30.0,945.79,googl -2017-08-07,-30.0,945.75,googl -2017-08-07,-30.0,945.75,googl -2017-08-08,-157.0,178.36,nflx -2017-08-08,-160.0,178.36,nflx -2017-08-08,-167.0,171.23,fb -2017-08-08,-167.0,171.23,fb -2017-08-09,-28.0,982.01,amzn -2017-08-09,-29.0,982.01,amzn -2017-08-10,-181.0,155.27,aapl -2017-08-10,-183.0,155.27,aapl -2017-08-11,160.0,171.4,nflx -2017-08-11,161.0,171.4,nflx -2017-08-11,29.0,967.99,amzn -2017-08-11,28.0,967.99,amzn -2017-08-11,167.0,168.08,fb -2017-08-11,165.0,168.08,fb -2017-08-11,183.0,157.48,aapl -2017-08-11,176.0,157.48,aapl -2017-08-11,30.0,930.09,googl -2017-08-11,29.0,930.09,googl -2017-08-14,-161.0,171.0,nflx -2017-08-14,-163.0,171.0,nflx -2017-08-15,-28.0,982.74,amzn -2017-08-15,-28.0,982.74,amzn -2017-08-15,-29.0,938.08,googl -2017-08-15,-30.0,938.08,googl -2017-08-16,163.0,169.98,nflx -2017-08-16,166.0,169.98,nflx -2017-08-16,-165.0,170.0,fb -2017-08-16,-166.0,170.0,fb -2017-08-16,-176.0,160.95,aapl -2017-08-16,-175.0,160.95,aapl -2017-08-16,30.0,944.27,googl -2017-08-16,29.0,944.27,googl -2017-08-17,-166.0,166.09,nflx -2017-08-17,-170.0,166.09,nflx -2017-08-17,-29.0,927.66,googl -2017-08-17,-30.0,927.66,googl -2017-08-18,170.0,166.54,nflx -2017-08-18,166.0,166.54,nflx -2017-08-18,166.0,167.41,fb -2017-08-18,165.0,167.41,fb -2017-08-22,28.0,966.9,amzn -2017-08-22,28.0,966.9,amzn -2017-08-22,175.0,159.78,aapl -2017-08-22,173.0,159.78,aapl -2017-08-22,30.0,940.4,googl -2017-08-22,29.0,940.4,googl -2017-08-23,-166.0,169.06,nflx -2017-08-23,-166.0,169.06,nflx -2017-08-23,-28.0,958.0,amzn -2017-08-23,-29.0,958.0,amzn -2017-08-23,-165.0,168.71,fb -2017-08-23,-166.0,168.71,fb -2017-08-24,-173.0,159.27,aapl -2017-08-24,-176.0,159.27,aapl -2017-08-24,-29.0,936.89,googl -2017-08-24,-29.0,936.89,googl -2017-08-25,176.0,159.86,aapl -2017-08-25,174.0,159.86,aapl -2017-08-28,166.0,167.12,nflx -2017-08-28,165.0,167.12,nflx -2017-08-28,29.0,946.02,amzn -2017-08-28,29.0,946.02,amzn -2017-08-28,166.0,167.24,fb -2017-08-28,165.0,167.24,fb -2017-08-29,29.0,935.75,googl -2017-08-29,29.0,935.75,googl -2017-09-01,-29.0,978.25,amzn -2017-09-01,-29.0,978.25,amzn -2017-09-01,-29.0,951.99,googl -2017-09-01,-30.0,951.99,googl -2017-09-05,-165.0,174.52,nflx -2017-09-05,-164.0,174.52,nflx -2017-09-05,-165.0,170.72,fb -2017-09-05,-167.0,170.72,fb -2017-09-05,-174.0,162.08,aapl -2017-09-05,-176.0,162.08,aapl -2017-09-06,164.0,179.25,nflx -2017-09-06,158.0,179.25,nflx -2017-09-06,29.0,967.8,amzn -2017-09-06,29.0,967.8,amzn -2017-09-06,167.0,172.09,fb -2017-09-06,165.0,172.09,fb -2017-09-06,30.0,942.02,googl -2017-09-06,30.0,942.02,googl -2017-09-07,-158.0,179.0,nflx -2017-09-07,-159.0,179.0,nflx -2017-09-08,-29.0,965.9,amzn -2017-09-08,-29.0,965.9,amzn -2017-09-08,-165.0,170.95,fb -2017-09-08,-168.0,170.95,fb -2017-09-08,-30.0,941.41,googl -2017-09-08,-30.0,941.41,googl -2017-09-11,159.0,181.74,nflx -2017-09-11,156.0,181.74,nflx -2017-09-11,29.0,977.96,amzn -2017-09-11,29.0,977.96,amzn -2017-09-11,168.0,173.51,fb -2017-09-11,163.0,173.51,fb -2017-09-11,176.0,161.5,aapl -2017-09-11,175.0,161.5,aapl -2017-09-11,30.0,943.29,googl -2017-09-11,30.0,943.29,googl -2017-09-12,-163.0,172.96,fb -2017-09-12,-166.0,172.96,fb -2017-09-12,-175.0,160.82,aapl -2017-09-12,-179.0,160.82,aapl -2017-09-13,-156.0,183.64,nflx -2017-09-13,-157.0,183.64,nflx -2017-09-13,166.0,173.05,fb -2017-09-13,167.0,173.05,fb -2017-09-14,-29.0,992.21,amzn -2017-09-14,-29.0,992.21,amzn -2017-09-14,-167.0,170.96,fb -2017-09-14,-169.0,170.96,fb -2017-09-14,-30.0,940.13,googl -2017-09-14,-30.0,940.13,googl -2017-09-15,169.0,171.58,fb -2017-09-15,167.0,171.58,fb -2017-09-15,179.0,159.88,aapl -2017-09-15,179.0,159.88,aapl -2017-09-18,157.0,184.62,nflx -2017-09-18,155.0,184.62,nflx -2017-09-18,-167.0,170.01,fb -2017-09-18,-168.0,170.01,fb -2017-09-18,-179.0,158.67,aapl -2017-09-18,-180.0,158.67,aapl -2017-09-19,168.0,172.52,fb -2017-09-19,165.0,172.52,fb -2017-09-19,180.0,158.73,aapl -2017-09-19,180.0,158.73,aapl -2017-09-19,30.0,936.86,googl -2017-09-19,30.0,936.86,googl -2017-09-20,-155.0,185.51,nflx -2017-09-20,-154.0,185.51,nflx -2017-09-20,29.0,973.21,amzn -2017-09-20,29.0,973.21,amzn -2017-09-20,-165.0,172.17,fb -2017-09-20,-166.0,172.17,fb -2017-09-20,-180.0,156.07,aapl -2017-09-20,-184.0,156.07,aapl -2017-09-21,154.0,188.78,nflx -2017-09-21,151.0,188.78,nflx -2017-09-21,-29.0,964.65,amzn -2017-09-21,-29.0,964.65,amzn -2017-09-22,-151.0,187.35,nflx -2017-09-22,-152.0,187.35,nflx -2017-09-22,-30.0,943.26,googl -2017-09-22,-30.0,943.26,googl -2017-09-26,152.0,179.38,nflx -2017-09-26,154.0,179.38,nflx -2017-09-26,166.0,164.21,fb -2017-09-26,168.0,164.21,fb -2017-09-26,184.0,153.14,aapl -2017-09-26,180.0,153.14,aapl -2017-09-26,30.0,937.43,googl -2017-09-26,29.0,937.43,googl -2017-09-27,29.0,950.87,amzn -2017-09-27,29.0,950.87,amzn -2017-09-28,-154.0,180.7,nflx -2017-09-28,-156.0,180.7,nflx -2017-09-28,-180.0,153.28,aapl -2017-09-28,-184.0,153.28,aapl -2017-09-29,156.0,181.35,nflx -2017-09-29,156.0,181.35,nflx -2017-09-29,184.0,154.12,aapl -2017-09-29,183.0,154.12,aapl -2017-10-02,-156.0,177.01,nflx -2017-10-02,-161.0,177.01,nflx -2017-10-02,-29.0,959.19,amzn -2017-10-02,-29.0,959.19,amzn -2017-10-02,-168.0,169.47,fb -2017-10-02,-168.0,169.47,fb -2017-10-02,-183.0,153.81,aapl -2017-10-02,-185.0,153.81,aapl -2017-10-02,-29.0,967.47,googl -2017-10-02,-29.0,967.47,googl -2017-10-03,161.0,179.19,nflx -2017-10-03,157.0,179.19,nflx -2017-10-03,168.0,169.96,fb -2017-10-03,166.0,169.96,fb -2017-10-03,185.0,154.48,aapl -2017-10-03,183.0,154.48,aapl -2017-10-03,29.0,972.08,googl -2017-10-03,29.0,972.08,googl -2017-10-04,29.0,965.45,amzn -2017-10-04,29.0,965.45,amzn -2017-10-04,-166.0,168.42,fb -2017-10-04,-168.0,168.42,fb -2017-10-04,-183.0,153.4508,aapl -2017-10-04,-185.0,153.4508,aapl -2017-10-04,-29.0,966.78,googl -2017-10-04,-29.0,966.78,googl -2017-10-05,168.0,171.24,fb -2017-10-05,166.0,171.24,fb -2017-10-05,185.0,155.39,aapl -2017-10-05,183.0,155.39,aapl -2017-10-05,29.0,985.19,googl -2017-10-05,28.0,985.19,googl -2017-10-06,-183.0,155.3,aapl -2017-10-06,-187.0,155.3,aapl -2017-10-09,-157.0,196.87,nflx -2017-10-09,-149.0,196.87,nflx -2017-10-09,187.0,155.84,aapl -2017-10-09,188.0,155.84,aapl -2017-10-09,-28.0,992.31,googl -2017-10-09,-29.0,992.31,googl -2017-10-10,-29.0,987.2,amzn -2017-10-10,-29.0,987.2,amzn -2017-10-10,-166.0,171.59,fb -2017-10-10,-171.0,171.59,fb -2017-10-11,29.0,995.0,amzn -2017-10-11,29.0,995.0,amzn -2017-10-11,171.0,172.74,fb -2017-10-11,169.0,172.74,fb -2017-10-11,29.0,1005.65,googl -2017-10-11,29.0,1005.65,googl -2017-10-12,149.0,195.86,nflx -2017-10-12,150.0,195.86,nflx -2017-10-12,-169.0,172.55,fb -2017-10-12,-170.0,172.55,fb -2017-10-12,-188.0,156.0,aapl -2017-10-12,-189.0,156.0,aapl -2017-10-13,170.0,173.74,fb -2017-10-13,170.0,173.74,fb -2017-10-13,189.0,156.99,aapl -2017-10-13,188.0,156.99,aapl -2017-10-17,-150.0,199.48,nflx -2017-10-17,-150.0,199.48,nflx -2017-10-18,-29.0,997.0,amzn -2017-10-18,-30.0,997.0,amzn -2017-10-18,-170.0,176.03,fb -2017-10-18,-170.0,176.03,fb -2017-10-18,-188.0,159.76,aapl -2017-10-18,-187.0,159.76,aapl -2017-10-19,-29.0,1001.84,googl -2017-10-19,-29.0,1001.84,googl -2017-10-20,170.0,174.98,fb -2017-10-20,168.0,174.98,fb -2017-10-20,187.0,156.16,aapl -2017-10-20,188.0,156.16,aapl -2017-10-20,29.0,1005.07,googl -2017-10-20,29.0,1005.07,googl -2017-10-23,-168.0,171.27,fb -2017-10-23,-172.0,171.27,fb -2017-10-23,-29.0,985.54,googl -2017-10-23,-29.0,985.54,googl -2017-10-24,150.0,196.02,nflx -2017-10-24,148.0,196.02,nflx -2017-10-24,30.0,975.9,amzn -2017-10-24,29.0,975.9,amzn -2017-10-24,172.0,171.8,fb -2017-10-24,169.0,171.8,fb -2017-10-24,29.0,988.49,googl -2017-10-24,29.0,988.49,googl -2017-10-25,-148.0,193.77,nflx -2017-10-25,-151.0,193.77,nflx -2017-10-25,-29.0,972.91,amzn -2017-10-25,-30.0,972.91,amzn -2017-10-25,-169.0,170.6,fb -2017-10-25,-171.0,170.6,fb -2017-10-25,-188.0,156.405,aapl -2017-10-25,-187.0,156.405,aapl -2017-10-26,151.0,195.21,nflx -2017-10-26,149.0,195.21,nflx -2017-10-26,171.0,170.63,fb -2017-10-26,170.0,170.63,fb -2017-10-26,187.0,157.41,aapl -2017-10-26,185.0,157.41,aapl -2017-10-26,-29.0,991.42,googl -2017-10-26,-29.0,991.42,googl -2017-10-27,30.0,1100.95,amzn -2017-10-27,26.0,1100.95,amzn -2017-10-27,29.0,1033.67,googl -2017-10-27,28.0,1033.67,googl -2017-10-30,-149.0,198.37,nflx -2017-10-30,-155.0,198.37,nflx -2017-10-30,-28.0,1033.13,googl -2017-10-30,-29.0,1033.13,googl -2017-10-31,-26.0,1105.28,amzn -2017-10-31,-28.0,1105.28,amzn -2017-11-01,155.0,198.0,nflx -2017-11-01,156.0,198.0,nflx -2017-11-01,-185.0,166.89,aapl -2017-11-01,-186.0,166.89,aapl -2017-11-01,29.0,1042.6,googl -2017-11-01,29.0,1042.6,googl -2017-11-02,-170.0,178.92,fb -2017-11-02,-174.0,178.92,fb -2017-11-02,186.0,168.11,aapl -2017-11-02,185.0,168.11,aapl -2017-11-03,28.0,1111.6,amzn -2017-11-03,27.0,1111.6,amzn -2017-11-06,174.0,180.17,fb -2017-11-06,174.0,180.17,fb -2017-11-06,-29.0,1042.68,googl -2017-11-06,-30.0,1042.68,googl -2017-11-07,-156.0,195.89,nflx -2017-11-07,-160.0,195.89,nflx -2017-11-07,30.0,1052.39,googl -2017-11-07,29.0,1052.39,googl -2017-11-09,-174.0,179.3,fb -2017-11-09,-175.0,179.3,fb -2017-11-09,-29.0,1047.72,googl -2017-11-09,-30.0,1047.72,googl -2017-11-10,-27.0,1125.35,amzn -2017-11-10,-27.0,1125.35,amzn -2017-11-10,-185.0,174.67,aapl -2017-11-10,-180.0,174.67,aapl -2017-11-13,160.0,195.08,nflx -2017-11-13,160.0,195.08,nflx -2017-11-13,27.0,1129.17,amzn -2017-11-13,27.0,1129.17,amzn -2017-11-13,175.0,178.77,fb -2017-11-13,174.0,178.77,fb -2017-11-14,-174.0,178.07,fb -2017-11-14,-176.0,178.07,fb -2017-11-14,30.0,1041.64,googl -2017-11-14,30.0,1041.64,googl -2017-11-15,-160.0,192.12,nflx -2017-11-15,-162.0,192.12,nflx -2017-11-15,-27.0,1126.69,amzn -2017-11-15,-27.0,1126.69,amzn -2017-11-15,-30.0,1036.41,googl -2017-11-15,-30.0,1036.41,googl -2017-11-16,162.0,195.51,nflx -2017-11-16,158.0,195.51,nflx -2017-11-16,27.0,1137.29,amzn -2017-11-16,27.0,1137.29,amzn -2017-11-16,176.0,179.59,fb -2017-11-16,172.0,179.59,fb -2017-11-16,180.0,171.1,aapl -2017-11-16,181.0,171.1,aapl -2017-11-16,30.0,1048.47,googl -2017-11-16,29.0,1048.47,googl -2017-11-17,-158.0,193.2,nflx -2017-11-17,-162.0,193.2,nflx -2017-11-17,-27.0,1129.88,amzn -2017-11-17,-27.0,1129.88,amzn -2017-11-17,-172.0,179.0,fb -2017-11-17,-175.0,179.0,fb -2017-11-17,-181.0,170.15,aapl -2017-11-17,-184.0,170.15,aapl -2017-11-17,-29.0,1035.89,googl -2017-11-17,-30.0,1035.89,googl -2017-11-20,162.0,194.1,nflx -2017-11-20,160.0,194.1,nflx -2017-11-21,27.0,1139.49,amzn -2017-11-21,27.0,1139.49,amzn -2017-11-21,175.0,181.86,fb -2017-11-21,171.0,181.86,fb -2017-11-21,184.0,173.14,aapl -2017-11-21,179.0,173.14,aapl -2017-11-21,30.0,1050.3,googl -2017-11-21,29.0,1050.3,googl -2017-11-22,-171.0,180.87,fb -2017-11-22,-174.0,180.87,fb -2017-11-24,-160.0,195.75,nflx -2017-11-24,-162.0,195.75,nflx -2017-11-24,174.0,182.78,fb -2017-11-24,173.0,182.78,fb -2017-11-27,-179.0,174.09,aapl -2017-11-27,-183.0,174.09,aapl -2017-11-28,162.0,199.18,nflx -2017-11-28,160.0,199.18,nflx -2017-11-28,-27.0,1193.6,amzn -2017-11-28,-26.0,1193.6,amzn -2017-11-28,-173.0,182.42,fb -2017-11-28,-175.0,182.42,fb -2017-11-28,-29.0,1063.29,googl -2017-11-28,-30.0,1063.29,googl -2017-11-29,-160.0,188.15,nflx -2017-11-29,-170.0,188.15,nflx -2017-11-30,26.0,1176.75,amzn -2017-11-30,26.0,1176.75,amzn -2017-11-30,175.0,177.18,fb -2017-11-30,174.0,177.18,fb -2017-11-30,183.0,171.85,aapl -2017-11-30,180.0,171.85,aapl -2017-12-01,-26.0,1162.35,amzn -2017-12-01,-26.0,1162.35,amzn -2017-12-01,-174.0,175.1,fb -2017-12-01,-178.0,175.1,fb -2017-12-01,-180.0,171.05,aapl -2017-12-01,-182.0,171.05,aapl -2017-12-05,170.0,184.21,nflx -2017-12-05,165.0,184.21,nflx -2017-12-05,26.0,1141.57,amzn -2017-12-05,26.0,1141.57,amzn -2017-12-05,178.0,172.83,fb -2017-12-05,176.0,172.83,fb -2017-12-05,30.0,1019.6,googl -2017-12-05,29.0,1019.6,googl -2017-12-07,-165.0,185.2,nflx -2017-12-07,-166.0,185.2,nflx -2017-12-07,182.0,169.452,aapl -2017-12-07,182.0,169.452,aapl -2017-12-08,166.0,188.54,nflx -2017-12-08,164.0,188.54,nflx -2017-12-08,-176.0,179.0,fb -2017-12-08,-173.0,179.0,fb -2017-12-08,-182.0,169.37,aapl -2017-12-08,-183.0,169.37,aapl -2017-12-11,-164.0,186.22,nflx -2017-12-11,-167.0,186.22,nflx -2017-12-11,173.0,179.04,fb -2017-12-11,174.0,179.04,fb -2017-12-11,183.0,172.67,aapl -2017-12-11,180.0,172.67,aapl -2017-12-12,-26.0,1165.08,amzn -2017-12-12,-26.0,1165.08,amzn -2017-12-12,-174.0,176.96,fb -2017-12-12,-176.0,176.96,fb -2017-12-12,-180.0,171.7,aapl -2017-12-12,-182.0,171.7,aapl -2017-12-12,-29.0,1048.77,googl -2017-12-12,-29.0,1048.77,googl -2017-12-13,167.0,187.86,nflx -2017-12-13,165.0,187.86,nflx -2017-12-13,176.0,178.3,fb -2017-12-13,174.0,178.3,fb -2017-12-13,182.0,172.27,aapl -2017-12-13,180.0,172.27,aapl -2017-12-13,29.0,1051.39,googl -2017-12-13,29.0,1051.39,googl -2017-12-14,26.0,1174.26,amzn -2017-12-14,26.0,1174.26,amzn -2017-12-14,-180.0,172.22,aapl -2017-12-14,-181.0,172.22,aapl -2017-12-15,181.0,173.87,aapl -2017-12-15,180.0,173.87,aapl -2017-12-19,-165.0,187.02,nflx -2017-12-19,-170.0,187.02,nflx -2017-12-19,-26.0,1187.38,amzn -2017-12-19,-26.0,1187.38,amzn -2017-12-19,-174.0,179.51,fb -2017-12-19,-177.0,179.51,fb -2017-12-19,-180.0,174.54,aapl -2017-12-19,-183.0,174.54,aapl -2017-12-19,-29.0,1079.78,googl -2017-12-19,-29.0,1079.78,googl -2017-12-20,170.0,188.82,nflx -2017-12-20,167.0,188.82,nflx -2017-12-21,-167.0,188.62,nflx -2017-12-21,-167.0,188.62,nflx -2017-12-21,183.0,175.01,aapl -2017-12-21,180.0,175.01,aapl -2017-12-22,167.0,189.94,nflx -2017-12-22,166.0,189.94,nflx -2017-12-26,-166.0,187.76,nflx -2017-12-26,-168.0,187.76,nflx -2017-12-26,26.0,1176.76,amzn -2017-12-26,26.0,1176.76,amzn -2017-12-26,-180.0,170.57,aapl -2017-12-26,-184.0,170.57,aapl -2017-12-27,177.0,177.62,fb -2017-12-27,176.0,177.62,fb -2017-12-27,184.0,170.6,aapl -2017-12-27,183.0,170.6,aapl -2017-12-28,168.0,192.71,nflx -2017-12-28,162.0,192.71,nflx -2017-12-29,-162.0,191.96,nflx -2017-12-29,-164.0,191.96,nflx -2017-12-29,-26.0,1169.47,amzn -2017-12-29,-26.0,1169.47,amzn -2017-12-29,-176.0,176.46,fb -2017-12-29,-178.0,176.46,fb -2017-12-29,-183.0,169.23,aapl -2017-12-29,-186.0,169.23,aapl diff --git a/alphapy/market_flow.py b/alphapy/market_flow.py index 2ceba55..b062f5d 100644 --- a/alphapy/market_flow.py +++ b/alphapy/market_flow.py @@ -49,6 +49,7 @@ import multiprocessing as mp import os import pandas as pd +import warnings import yaml @@ -428,5 +429,6 @@ def main(args=None): # if __name__ == "__main__": + warnings.filterwarnings(action='ignore', category=DeprecationWarning) mp.set_start_method('forkserver') main() diff --git a/alphapy/model.py b/alphapy/model.py index c84309c..822ad0b 100644 --- a/alphapy/model.py +++ b/alphapy/model.py @@ -43,6 +43,7 @@ from copy import copy from datetime import datetime +from keras.models import load_model import logging import numpy as np import pandas as pd @@ -53,7 +54,9 @@ from sklearn.metrics import accuracy_score from sklearn.metrics import auc from sklearn.metrics import average_precision_score +from sklearn.metrics import brier_score_loss from sklearn.metrics import classification_report +from sklearn.metrics import cohen_kappa_score from sklearn.metrics import confusion_matrix from sklearn.metrics import explained_variance_score from sklearn.metrics import f1_score @@ -154,6 +157,7 @@ def __init__(self, self.algolist = self.specs['algorithms'] except: raise KeyError("Model specs must include the key: algorithms") + self.best_algo = None # feature map self.feature_map = {} # Key: (algorithm) @@ -307,7 +311,6 @@ def get_model_config(): # Section: model specs['algorithms'] = cfg['model']['algorithms'] - specs['balance_classes'] = cfg['model']['balance_classes'] specs['cv_folds'] = cfg['model']['cv_folds'] # determine whether or not model type is valid model_types = {x.name: x.value for x in ModelType} @@ -373,7 +376,6 @@ def get_model_config(): logger.info('MODEL PARAMETERS:') logger.info('algorithms = %s', specs['algorithms']) - logger.info('balance_classes = %s', specs['balance_classes']) logger.info('calibration = %r', specs['calibration']) logger.info('cal_type = %s', specs['cal_type']) logger.info('calibration_plot = %r', specs['calibration']) @@ -474,15 +476,22 @@ def load_predictor(directory): """ - # Locate the model Pickle file + # Locate the model Pickle or HD5 file - try: - search_dir = SSEP.join([directory, 'model']) - file_name = most_recent_file(search_dir, 'model_*.pkl') + search_dir = SSEP.join([directory, 'model']) + file_name = most_recent_file(search_dir, 'model_*.*') + + # Load the model from the file + + file_ext = file_name.split(PSEP)[-1] + if file_ext == 'pkl' or file_ext == 'h5': logger.info("Loading model predictor from %s", file_name) # load the model predictor - predictor = joblib.load(file_name) - except: + if file_ext == 'pkl': + predictor = joblib.load(file_name) + elif file_ext == 'h5': + predictor = load_model(file_name) + else: logging.error("Could not find model predictor in %s", search_path) # Return the model predictor @@ -517,15 +526,18 @@ def save_predictor(model, timestamp): # Get the best predictor predictor = model.estimators['BEST'] - # Create full path name. - - filename = 'model_' + timestamp + '.pkl' - full_path = SSEP.join([directory, 'model', filename]) - # Save model object - logger.info("Writing model predictor to %s", full_path) - joblib.dump(predictor, full_path) + if 'KERAS' in model.best_algo: + filename = 'model_' + timestamp + '.h5' + full_path = SSEP.join([directory, 'model', filename]) + logger.info("Writing model predictor to %s", full_path) + predictor.model.save(full_path) + else: + filename = 'model_' + timestamp + '.pkl' + full_path = SSEP.join([directory, 'model', filename]) + logger.info("Writing model predictor to %s", full_path) + joblib.dump(predictor, full_path) # @@ -602,56 +614,6 @@ def save_feature_map(model, timestamp): joblib.dump(model.feature_map, full_path) -# -# Function get_class_weights -# - -def get_class_weights(model): - r"""Set the class weights for fitting the model. - - Parameters - ---------- - model : alphapy.Model - The model object with specifications. - - Returns - ------- - model : alphapy.Model - The model object with class weights. - - """ - - # Extract model parameters. - - balance_classes = model.specs['balance_classes'] - target = model.specs['target'] - target_value = model.specs['target_value'] - - # Extract model data. - - y_train = model.y_train - - # Calculate sample weights - - sw = None - if balance_classes: - logger.info("Getting Class Weights") - uv, uc = np.unique(y_train, return_counts=True) - target_index = np.where(uv == target_value)[0][0] - nontarget_index = np.where(uv != target_value)[0][0] - weight = uc[nontarget_index] / uc[target_index] - logger.info("Class Weight for target %s [%r]: %f", - target, target_value, weight) - sw = [weight if x==target_value else 1.0 for x in y_train] - else: - logger.info("Skipping Class Weights") - - # Set weights - - model.specs['class_weights'] = sw - return model - - # # Function first_fit # @@ -693,13 +655,6 @@ def first_fit(model, algo, est): seed = model.specs['seed'] split = model.specs['split'] - # Initialize class weights. - - if model_type == ModelType.classification: - class_weights = model.specs['class_weights'] - else: - class_weights = None - # Extract model data. X_train = model.X_train @@ -707,15 +662,16 @@ def first_fit(model, algo, est): # Fit the initial model. - if 'XGB' in algo and scorer in xgb_score_map: + algo_keras = 'KERAS' in algo + algo_xgb = 'XGB' in algo + + if algo_xgb and scorer in xgb_score_map: X1, X2, y1, y2 = train_test_split(X_train, y_train, test_size=split, random_state=seed) eval_set = [(X1, y1), (X2, y2)] eval_metric = xgb_score_map[scorer] est.fit(X1, y1, eval_set=eval_set, eval_metric=eval_metric, early_stopping_rounds=esr) - elif class_weights and model_type == ModelType.classification: - est.fit(X_train, y_train, sample_weight=class_weights) else: est.fit(X_train, y_train) @@ -772,13 +728,6 @@ def make_predictions(model, algo, calibrate): cv_folds = model.specs['cv_folds'] model_type = model.specs['model_type'] - # Initialize class weights. - - if model_type == ModelType.classification: - class_weights = model.specs['class_weights'] - else: - class_weights = None - # Get the estimator est = model.estimators[algo] @@ -800,7 +749,7 @@ def make_predictions(model, algo, calibrate): if calibrate: logger.info("Calibrating Classifier") est = CalibratedClassifierCV(est, cv=cv_folds, method=cal_type) - est.fit(X_train, y_train, sample_weight=class_weights) + est.fit(X_train, y_train) model.estimators[algo] = est logger.info("Calibration Complete") else: @@ -910,6 +859,7 @@ def predict_best(model): # Record predictions of best estimator logger.info("Best Model is %s with a %s score of %.4f", best_algo, scorer, best_score) + model.best_algo = best_algo model.estimators[best_tag] = model.estimators[best_algo] model.preds[(best_tag, Partition.train)] = model.preds[(best_algo, Partition.train)] model.preds[(best_tag, Partition.test)] = model.preds[(best_algo, Partition.test)] @@ -1094,66 +1044,72 @@ def generate_metrics(model, partition): for algo in algolist: # get predictions for the given algorithm predicted = model.preds[(algo, partition)] - try: - model.metrics[(algo, partition, 'accuracy')] = accuracy_score(expected, predicted) - except: - logger.info("Accuracy Score not calculated") - try: - model.metrics[(algo, partition, 'adjusted_rand_score')] = adjusted_rand_score(expected, predicted) - except: - logger.info("Adjusted Rand Index not calculated") - try: - model.metrics[(algo, partition, 'confusion_matrix')] = confusion_matrix(expected, predicted) - except: - logger.info("Confusion Matrix not calculated") - try: - model.metrics[(algo, partition, 'explained_variance')] = explained_variance_score(expected, predicted) - except: - logger.info("Explained Variance Score not calculated") - try: - model.metrics[(algo, partition, 'f1')] = f1_score(expected, predicted) - except: - logger.info("F1 Score not calculated") - try: - model.metrics[(algo, partition, 'mean_absolute_error')] = mean_absolute_error(expected, predicted) - except: - logger.info("Mean Absolute Error not calculated") - try: - model.metrics[(algo, partition, 'median_absolute_error')] = median_absolute_error(expected, predicted) - except: - logger.info("Median Absolute Error not calculated") - try: - model.metrics[(algo, partition, 'neg_mean_squared_error')] = mean_squared_error(expected, predicted) - except: - logger.info("Mean Squared Error not calculated") - try: - model.metrics[(algo, partition, 'precision')] = precision_score(expected, predicted) - except: - logger.info("Precision Score not calculated") - try: - model.metrics[(algo, partition, 'r2')] = r2_score(expected, predicted) - except: - logger.info("R-Squared Score not calculated") - try: - model.metrics[(algo, partition, 'recall')] = recall_score(expected, predicted) - except: - logger.info("Recall Score not calculated") - # Probability-Based Metrics + # classification metrics if model_type == ModelType.classification: - predicted = model.probas[(algo, partition)] + probas = model.probas[(algo, partition)] + try: + model.metrics[(algo, partition, 'accuracy')] = accuracy_score(expected, predicted) + except: + logger.info("Accuracy Score not calculated") try: - model.metrics[(algo, partition, 'average_precision')] = average_precision_score(expected, predicted) + model.metrics[(algo, partition, 'average_precision')] = average_precision_score(expected, probas) except: logger.info("Average Precision Score not calculated") try: - model.metrics[(algo, partition, 'neg_log_loss')] = log_loss(expected, predicted) + model.metrics[(algo, partition, 'brier_score')] = brier_score_loss(expected, probas) + except: + logger.info("Brier Score not calculated") + try: + model.metrics[(algo, partition, 'cohen_kappa')] = cohen_kappa_score(expected, predicted) + except: + logger.info("Cohen's Kappa Score not calculated") + try: + model.metrics[(algo, partition, 'confusion_matrix')] = confusion_matrix(expected, predicted) + except: + logger.info("Confusion Matrix not calculated") + try: + model.metrics[(algo, partition, 'f1')] = f1_score(expected, predicted) + except: + logger.info("F1 Score not calculated") + try: + model.metrics[(algo, partition, 'neg_log_loss')] = log_loss(expected, probas) except: logger.info("Log Loss not calculated") try: - fpr, tpr, _ = roc_curve(expected, predicted) + model.metrics[(algo, partition, 'precision')] = precision_score(expected, predicted) + except: + logger.info("Precision Score not calculated") + try: + model.metrics[(algo, partition, 'recall')] = recall_score(expected, predicted) + except: + logger.info("Recall Score not calculated") + try: + fpr, tpr, _ = roc_curve(expected, probas) model.metrics[(algo, partition, 'roc_auc')] = auc(fpr, tpr) except: logger.info("ROC AUC Score not calculated") + # regression metrics + elif model_type == ModelType.regression: + try: + model.metrics[(algo, partition, 'explained_variance')] = explained_variance_score(expected, predicted) + except: + logger.info("Explained Variance Score not calculated") + try: + model.metrics[(algo, partition, 'mean_absolute_error')] = mean_absolute_error(expected, predicted) + except: + logger.info("Mean Absolute Error not calculated") + try: + model.metrics[(algo, partition, 'median_absolute_error')] = median_absolute_error(expected, predicted) + except: + logger.info("Median Absolute Error not calculated") + try: + model.metrics[(algo, partition, 'neg_mean_squared_error')] = mean_squared_error(expected, predicted) + except: + logger.info("Mean Squared Error not calculated") + try: + model.metrics[(algo, partition, 'r2')] = r2_score(expected, predicted) + except: + logger.info("R-Squared Score not calculated") # log the metrics for each algorithm for algo in model.algolist: logger.info('-'*80) @@ -1230,7 +1186,7 @@ def save_predictions(model, tag, partition): logger.info("Saving Predictions") output_file = USEP.join(['predictions', timestamp]) - preds = model.preds[(tag, partition)] + preds = model.preds[(tag, partition)].squeeze() if found_pdate: preds = np.take(preds, pd_indices) pred_series = pd.Series(preds, index=pd_indices) @@ -1243,7 +1199,7 @@ def save_predictions(model, tag, partition): if model_type == ModelType.classification: logger.info("Saving Probabilities") output_file = USEP.join(['probabilities', timestamp]) - probas = model.probas[(tag, partition)] + probas = model.probas[(tag, partition)].squeeze() if found_pdate: probas = np.take(probas, pd_indices) prob_series = pd.Series(probas, index=pd_indices) diff --git a/alphapy/optimize.py b/alphapy/optimize.py index e5a601e..39d316a 100644 --- a/alphapy/optimize.py +++ b/alphapy/optimize.py @@ -123,76 +123,6 @@ def rfecv_search(model, algo): return model -# -# Function rfe_search -# - -def rfe_search(model, algo): - r"""Return the best feature set using recursive feature elimination. - - Parameters - ---------- - model : alphapy.Model - The model object with RFE parameters. - algo : str - Abbreviation of the algorithm to run. - - Returns - ------- - model : alphapy.Model - The model object with the RFE support vector and the best - estimator. - - See Also - -------- - rfecv_search - - Notes - ----- - If a scoring function is available, then AlphaPy can perform RFE - with Cross-Validation (CV); otherwise, it just does RFE without CV, - as in this function. - - References - ---------- - For more information about Recursive Feature Elimination, - refer to [RFE]_. - - .. [RFE] http://scikit-learn.org/stable/modules/feature_selection.html#recursive-feature-elimination - - """ - - # Extract model data. - - X_train = model.X_train - y_train = model.y_train - - # Extract model parameters. - - rfe_step = model.specs['rfe_step'] - verbosity = model.specs['verbosity'] - estimator = model.estimators[algo] - - # Perform Recursive Feature Elimination - - logger.info("Recursive Feature Elimination") - rfe = RFE(estimator, step=rfe_step, verbose=verbosity) - start = time() - selector = rfe.fit(X_train, y_train) - logger.info("RFE took %.2f seconds for step %d", - (time() - start), rfe_step) - logger.info("Algorithm: %s, Selected Features: %d, Ranking: %s", - algo, selector.n_features_, selector.ranking_) - - # Record the new estimator and support vector - - model.estimators[algo] = selector.estimator_ - model.support[algo] = selector.support_ - - # Return the model with the support vector - return model - - # # Function grid_report # diff --git a/alphapy/plots.py b/alphapy/plots.py index 2b2f6b7..ada107d 100644 --- a/alphapy/plots.py +++ b/alphapy/plots.py @@ -77,7 +77,6 @@ from sklearn.calibration import calibration_curve from sklearn.ensemble.partial_dependence import partial_dependence from sklearn.ensemble.partial_dependence import plot_partial_dependence -from sklearn.learning_curve import validation_curve from sklearn.metrics import auc from sklearn.metrics import confusion_matrix from sklearn.metrics import roc_curve @@ -85,6 +84,7 @@ from sklearn.model_selection import learning_curve from sklearn.model_selection import StratifiedKFold from sklearn.model_selection import train_test_split +from sklearn.model_selection import validation_curve # diff --git a/alphapy/sport_flow.py b/alphapy/sport_flow.py index 9dda5ab..2bf823d 100644 --- a/alphapy/sport_flow.py +++ b/alphapy/sport_flow.py @@ -49,6 +49,7 @@ import numpy as np import os import pandas as pd +import warnings import yaml @@ -912,5 +913,6 @@ def main(args=None): # if __name__ == "__main__": + warnings.filterwarnings(action='ignore', category=DeprecationWarning) mp.set_start_method('forkserver') main() From 2cccd21420ccc97188ae96ebb71c8959f11ebdcf Mon Sep 17 00:00:00 2001 From: Mark Conway Date: Mon, 21 May 2018 10:54:52 -0400 Subject: [PATCH 018/129] update package requirements update package requirements --- setup.py | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/setup.py b/setup.py index fa27d41..113e4ba 100644 --- a/setup.py +++ b/setup.py @@ -10,7 +10,7 @@ MAINTAINER_EMAIL = 'scottfree.analytics@scottfreellc.com' URL = "https://github.com/ScottFreeLLC/AlphaPy" LICENSE = "Apache License, Version 2" -VERSION = "2.2.2" +VERSION = "2.3" classifiers = ['Development Status :: 4 - Beta', 'Programming Language :: Python', @@ -29,15 +29,15 @@ 'imbalanced-learn>=0.2.1', 'ipython>=3.2.3', 'matplotlib>=2.0.0', - 'numpy>=1.9.1', - 'pandas>=0.19.0', + 'numpy>=1.12', + 'pandas>=0.21.0', 'pandas-datareader>=0.3', - 'pyfolio>=0.7', + 'pyfolio>=0.8', 'pyyaml>=3.12', - 'scikit-learn>=0.17.1', - 'scipy>=0.18.1', + 'scikit-learn>=0.18', + 'scipy>=1.0', 'seaborn>=0.7.1', - 'xgboost>=0.6a2', + 'xgboost>=0.71', ] if __name__ == "__main__": From c65905cf1c517d2b2d8596773b04b7930af1d153 Mon Sep 17 00:00:00 2001 From: Mark Conway Date: Mon, 21 May 2018 11:39:16 -0400 Subject: [PATCH 019/129] pandas-datareader>=0.6 pandas-datareader>=0.6 --- setup.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/setup.py b/setup.py index 113e4ba..0dc4ec0 100644 --- a/setup.py +++ b/setup.py @@ -31,7 +31,7 @@ 'matplotlib>=2.0.0', 'numpy>=1.12', 'pandas>=0.21.0', - 'pandas-datareader>=0.3', + 'pandas-datareader>=0.6', 'pyfolio>=0.8', 'pyyaml>=3.12', 'scikit-learn>=0.18', From b3b4850669974814c84732127e94181bf91b32d6 Mon Sep 17 00:00:00 2001 From: Mark Conway Date: Mon, 21 May 2018 11:54:56 -0400 Subject: [PATCH 020/129] don't use pandas 0.23 pandas 0.23 is incompatible, so limit to versions between 0.19 and 0.22 --- setup.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/setup.py b/setup.py index 0dc4ec0..6df4d0b 100644 --- a/setup.py +++ b/setup.py @@ -30,7 +30,7 @@ 'ipython>=3.2.3', 'matplotlib>=2.0.0', 'numpy>=1.12', - 'pandas>=0.21.0', + 'pandas>=0.19,<=0.22', 'pandas-datareader>=0.6', 'pyfolio>=0.8', 'pyyaml>=3.12', From 010059d25c207eab828002f948e0b3075b53fdae Mon Sep 17 00:00:00 2001 From: Mark Conway Date: Mon, 21 May 2018 11:59:16 -0400 Subject: [PATCH 021/129] version 2.3.1 version 2.3.1 --- setup.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/setup.py b/setup.py index 6df4d0b..4bf40b9 100644 --- a/setup.py +++ b/setup.py @@ -10,7 +10,7 @@ MAINTAINER_EMAIL = 'scottfree.analytics@scottfreellc.com' URL = "https://github.com/ScottFreeLLC/AlphaPy" LICENSE = "Apache License, Version 2" -VERSION = "2.3" +VERSION = "2.3.1" classifiers = ['Development Status :: 4 - Beta', 'Programming Language :: Python', From 2a5a58cd3262e4f4045173c30868c0d765c7d43b Mon Sep 17 00:00:00 2001 From: Mark Conway Date: Wed, 23 May 2018 16:19:51 -0400 Subject: [PATCH 022/129] Create .travis.yml --- .travis.yml | 6 ++++++ 1 file changed, 6 insertions(+) create mode 100644 .travis.yml diff --git a/.travis.yml b/.travis.yml new file mode 100644 index 0000000..7d02f94 --- /dev/null +++ b/.travis.yml @@ -0,0 +1,6 @@ +language: python +sudo: false + +python: + - 3.5 + - 3.6 From d75f6f3126439352509bbbdabd0a8504b9275726 Mon Sep 17 00:00:00 2001 From: Mark Conway Date: Wed, 23 May 2018 16:22:39 -0400 Subject: [PATCH 023/129] Update .travis.yml --- .travis.yml | 5 ----- 1 file changed, 5 deletions(-) diff --git a/.travis.yml b/.travis.yml index 7d02f94..d1ad0ae 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,6 +1 @@ language: python -sudo: false - -python: - - 3.5 - - 3.6 From 17aeaf4eff7141670e5a58dc035e72df43e60882 Mon Sep 17 00:00:00 2001 From: Mark Conway Date: Wed, 23 May 2018 16:24:58 -0400 Subject: [PATCH 024/129] Update .travis.yml --- .travis.yml | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/.travis.yml b/.travis.yml index d1ad0ae..832a1e7 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1 +1,6 @@ language: python +sudo: false + +python: + - 3.5 + - 3.6 From d2ad3d5c8756216fca0e52ad385313c69f8330ff Mon Sep 17 00:00:00 2001 From: Mark Conway Date: Fri, 25 May 2018 17:48:10 -0400 Subject: [PATCH 025/129] Update .travis.yml --- .travis.yml | 32 +++++++++++++++++++++++++++++--- 1 file changed, 29 insertions(+), 3 deletions(-) diff --git a/.travis.yml b/.travis.yml index 832a1e7..54937e6 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,6 +1,32 @@ language: python sudo: false -python: - - 3.5 - - 3.6 +python: + - "3.5" + - "3.6" + +before_install: + # We do this conditionally because it saves us some downloading if the + # version is the same. + - if [[ "$TRAVIS_PYTHON_VERSION" == "2.7" ]]; then + wget https://repo.continuum.io/miniconda/Miniconda2-latest-Linux-x86_64.sh -O miniconda.sh; + else + wget https://repo.continuum.io/miniconda/Miniconda3-latest-Linux-x86_64.sh -O miniconda.sh; + fi + - bash miniconda.sh -b -p $HOME/miniconda + - export PATH="$HOME/miniconda/bin:$PATH" + - hash -r + - conda config --set always_yes yes --set changeps1 no + - conda update -q conda + # Useful for debugging any issues with conda + - conda info -a + +install: + # Replace dep1 dep2 ... with your dependencies + - conda create -q -n testenv python=$TRAVIS_PYTHON_VERSION bokeh ipython matplotlib numpy pandas pyyaml scikit-learn scipy seaborn xgboost category_encoders imbalanced-learn pandas-datareader pyfolio + - source activate testenv + - python setup.py install + +branches: + only: + - master From 098abc94aaa4ad3a5796b9c10a4a1c1abdb2f005 Mon Sep 17 00:00:00 2001 From: Mark Conway Date: Fri, 25 May 2018 18:01:35 -0400 Subject: [PATCH 026/129] Update .travis.yml --- .travis.yml | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/.travis.yml b/.travis.yml index 54937e6..891af6a 100644 --- a/.travis.yml +++ b/.travis.yml @@ -25,8 +25,9 @@ install: # Replace dep1 dep2 ... with your dependencies - conda create -q -n testenv python=$TRAVIS_PYTHON_VERSION bokeh ipython matplotlib numpy pandas pyyaml scikit-learn scipy seaborn xgboost category_encoders imbalanced-learn pandas-datareader pyfolio - source activate testenv - - python setup.py install - + - pip install category_encoders + - pip install imbalanced-learn + - pip install pyfolio branches: only: - master From c4c97e2ff95638be4559256d23e31a40eb634d59 Mon Sep 17 00:00:00 2001 From: Mark Conway Date: Fri, 25 May 2018 18:03:47 -0400 Subject: [PATCH 027/129] Update .travis.yml --- .travis.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.travis.yml b/.travis.yml index 891af6a..24582b5 100644 --- a/.travis.yml +++ b/.travis.yml @@ -23,7 +23,7 @@ before_install: install: # Replace dep1 dep2 ... with your dependencies - - conda create -q -n testenv python=$TRAVIS_PYTHON_VERSION bokeh ipython matplotlib numpy pandas pyyaml scikit-learn scipy seaborn xgboost category_encoders imbalanced-learn pandas-datareader pyfolio + - conda create -q -n testenv python=$TRAVIS_PYTHON_VERSION bokeh ipython matplotlib numpy pandas pyyaml scikit-learn scipy seaborn pandas-datareader - source activate testenv - pip install category_encoders - pip install imbalanced-learn From e9e345ed828a72a74b67ae1f7022e513b814d815 Mon Sep 17 00:00:00 2001 From: Mark Conway Date: Fri, 25 May 2018 18:18:42 -0400 Subject: [PATCH 028/129] Update .travis.yml --- .travis.yml | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/.travis.yml b/.travis.yml index 24582b5..7fcc0c1 100644 --- a/.travis.yml +++ b/.travis.yml @@ -28,6 +28,10 @@ install: - pip install category_encoders - pip install imbalanced-learn - pip install pyfolio + +script: + # Add test coverage + branches: only: - master From 6325cdef4e20ff94fbcd95a92b771feb2d12f1bc Mon Sep 17 00:00:00 2001 From: Mark Conway Date: Fri, 25 May 2018 18:35:45 -0400 Subject: [PATCH 029/129] Update .travis.yml --- .travis.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.travis.yml b/.travis.yml index 7fcc0c1..22e1061 100644 --- a/.travis.yml +++ b/.travis.yml @@ -30,7 +30,7 @@ install: - pip install pyfolio script: - # Add test coverage + nosetests branches: only: From f7f0f142cf5311bd09f2d6aec82b87fe8d6c8745 Mon Sep 17 00:00:00 2001 From: Mark Conway Date: Fri, 25 May 2018 18:39:58 -0400 Subject: [PATCH 030/129] Update .travis.yml --- .travis.yml | 3 +++ 1 file changed, 3 insertions(+) diff --git a/.travis.yml b/.travis.yml index 22e1061..6cc6a2a 100644 --- a/.travis.yml +++ b/.travis.yml @@ -32,6 +32,9 @@ install: script: nosetests +notifications: + email: false + branches: only: - master From 70305bba74ce5bfbc7b7bdb0e5c546ab613aad30 Mon Sep 17 00:00:00 2001 From: Mark Conway Date: Fri, 25 May 2018 18:44:37 -0400 Subject: [PATCH 031/129] badges add badges for PyPi and Travis --- README.rst | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/README.rst b/README.rst index 4eb5b3c..0331940 100644 --- a/README.rst +++ b/README.rst @@ -1,3 +1,8 @@ +.. image:: https://badge.fury.io/py/alphapy.svg + :target: https://badge.fury.io/py/alphapy + +.. image:: https://travis-ci.org/ScottFreeLLC/AlphaPy.svg?branch=master + AlphaPy ======= From 5b1dc8e05710fc676147fe3606c840ecc7d1037c Mon Sep 17 00:00:00 2001 From: Mark Conway Date: Fri, 25 May 2018 18:48:10 -0400 Subject: [PATCH 032/129] note Keras in documentation note Keras in documentation --- README.rst | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/README.rst b/README.rst index 0331940..e2965e9 100644 --- a/README.rst +++ b/README.rst @@ -7,10 +7,10 @@ AlphaPy ======= **AlphaPy** is a machine learning framework for both speculators and -data scientists. It is written in Python with the ``scikit-learn`` -and ``pandas`` libraries, as well as many other helpful libraries -for feature engineering and visualization. Here are just some of the -things you can do with AlphaPy: +data scientists. It is written in Python with the ``scikit-learn``, +``pandas``, and ``Keras`` libraries, as well as many other helpful +libraries for feature engineering and visualization. Here are just +some of the things you can do with AlphaPy: * Run machine learning models using ``scikit-learn`` and ``xgboost``. * Create models for analyzing the markets with *MarketFlow*. From aeff222d3f4283773dc6265a38ebe33da5799b52 Mon Sep 17 00:00:00 2001 From: Mark Conway Date: Fri, 25 May 2018 18:49:30 -0400 Subject: [PATCH 033/129] Keras in README Keras in README --- README.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.rst b/README.rst index e2965e9..0d70646 100644 --- a/README.rst +++ b/README.rst @@ -12,7 +12,7 @@ data scientists. It is written in Python with the ``scikit-learn``, libraries for feature engineering and visualization. Here are just some of the things you can do with AlphaPy: -* Run machine learning models using ``scikit-learn`` and ``xgboost``. +* Run machine learning models using ``scikit-learn``, ``xgboost``, and ``Keras``. * Create models for analyzing the markets with *MarketFlow*. * Predict sporting events with *SportFlow*. * Develop trading systems and analyze portfolios using *MarketFlow* From f1da6bf1bf09561b68a39a1f9d7a18131765ad88 Mon Sep 17 00:00:00 2001 From: Mark Conway Date: Thu, 2 Aug 2018 14:06:25 -0400 Subject: [PATCH 034/129] Update README.rst --- README.rst | 2 -- 1 file changed, 2 deletions(-) diff --git a/README.rst b/README.rst index d8ca18b..b05148d 100644 --- a/README.rst +++ b/README.rst @@ -4,8 +4,6 @@ .. image:: https://travis-ci.org/ScottFreeLLC/AlphaPy.svg?branch=master .. image:: https://readthedocs.org/projects/alphapy/badge/?version=latest -:target: https://alphapy.readthedocs.io/en/latest/?badge=latest -:alt: Documentation Status AlphaPy ======= From 61af1656cab7c56c7abc274b5e14e894064f68c6 Mon Sep 17 00:00:00 2001 From: Mark Conway Date: Thu, 2 Aug 2018 14:06:31 -0400 Subject: [PATCH 035/129] documentation badge documentation badge --- README.rst | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/README.rst b/README.rst index 0d70646..d8ca18b 100644 --- a/README.rst +++ b/README.rst @@ -3,6 +3,10 @@ .. image:: https://travis-ci.org/ScottFreeLLC/AlphaPy.svg?branch=master +.. image:: https://readthedocs.org/projects/alphapy/badge/?version=latest +:target: https://alphapy.readthedocs.io/en/latest/?badge=latest +:alt: Documentation Status + AlphaPy ======= From b4c7ad66a238d33af5bcfc5524a5845e4acc3f44 Mon Sep 17 00:00:00 2001 From: Mark Conway Date: Thu, 2 Aug 2018 14:09:05 -0400 Subject: [PATCH 036/129] documentation badge for RTD documentation badge for RTD --- README.rst | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/README.rst b/README.rst index d8ca18b..0c62ced 100644 --- a/README.rst +++ b/README.rst @@ -4,8 +4,8 @@ .. image:: https://travis-ci.org/ScottFreeLLC/AlphaPy.svg?branch=master .. image:: https://readthedocs.org/projects/alphapy/badge/?version=latest -:target: https://alphapy.readthedocs.io/en/latest/?badge=latest -:alt: Documentation Status + :target: https://alphapy.readthedocs.io/en/latest/?badge=latest + :alt: Documentation Status AlphaPy ======= From ba9f558ac0279cb1e15186114014c8eb8c0beb6e Mon Sep 17 00:00:00 2001 From: Mark Conway Date: Thu, 2 Aug 2018 14:08:50 -0400 Subject: [PATCH 037/129] Update README.rst --- README.rst | 3 --- 1 file changed, 3 deletions(-) diff --git a/README.rst b/README.rst index 325d9ad..0c62ced 100644 --- a/README.rst +++ b/README.rst @@ -4,11 +4,8 @@ .. image:: https://travis-ci.org/ScottFreeLLC/AlphaPy.svg?branch=master .. image:: https://readthedocs.org/projects/alphapy/badge/?version=latest -<<<<<<< HEAD :target: https://alphapy.readthedocs.io/en/latest/?badge=latest :alt: Documentation Status -======= ->>>>>>> f1da6bf1bf09561b68a39a1f9d7a18131765ad88 AlphaPy ======= From 610c08acde08154f6c8a323c891c8cc5332861a0 Mon Sep 17 00:00:00 2001 From: Mark Conway Date: Thu, 2 Aug 2018 14:23:15 -0400 Subject: [PATCH 038/129] badge horizontal layout badge horizontal layout --- README.rst | 16 +++++++--------- 1 file changed, 7 insertions(+), 9 deletions(-) diff --git a/README.rst b/README.rst index 0c62ced..0ed9fc0 100644 --- a/README.rst +++ b/README.rst @@ -1,15 +1,8 @@ -.. image:: https://badge.fury.io/py/alphapy.svg - :target: https://badge.fury.io/py/alphapy - -.. image:: https://travis-ci.org/ScottFreeLLC/AlphaPy.svg?branch=master - -.. image:: https://readthedocs.org/projects/alphapy/badge/?version=latest - :target: https://alphapy.readthedocs.io/en/latest/?badge=latest - :alt: Documentation Status - AlphaPy ======= +|badge_pypi| |badge_build| |badge_docs| + **AlphaPy** is a machine learning framework for both speculators and data scientists. It is written in Python with the ``scikit-learn``, ``pandas``, and ``Keras`` libraries, as well as many other helpful @@ -86,3 +79,8 @@ Donations If you like the software, please donate: http://alphapy.readthedocs.io/en/latest/introduction/support.html#donations + + +.. |badge_pypi| image:: https://badge.fury.io/py/alphapy.svg +.. |badge_build| image:: https://travis-ci.org/ScottFreeLLC/AlphaPy.svg?branch=master +.. |badge_docs| image:: https://readthedocs.org/projects/alphapy/badge/?version=latest \ No newline at end of file From 26b85bd7ad9719ebcfa85ef40bbbd8c51c44ef7f Mon Sep 17 00:00:00 2001 From: Mark Conway Date: Wed, 15 Aug 2018 20:48:01 -0400 Subject: [PATCH 039/129] update version with pandas fix update version with pandas fix --- alphapy/data.py | 1 + docs/conf.py | 8 ++++---- environment.yml | 2 +- setup.py | 4 ++-- 4 files changed, 8 insertions(+), 7 deletions(-) diff --git a/alphapy/data.py b/alphapy/data.py index db5fe53..d9e0bb7 100644 --- a/alphapy/data.py +++ b/alphapy/data.py @@ -58,6 +58,7 @@ import logging import numpy as np import pandas as pd +pd.core.common.is_list_like = pd.api.types.is_list_like import pandas_datareader.data as web import re import requests diff --git a/docs/conf.py b/docs/conf.py index 1d66e86..45fe40f 100644 --- a/docs/conf.py +++ b/docs/conf.py @@ -54,7 +54,7 @@ # General information about the project. project = 'AlphaPy' -copyright = '2017, ScottFree Analytics LLC' +copyright = '2018, ScottFree Analytics LLC' author = 'Robert D. Scott II, Mark Conway' # The version info for the project you're documenting, acts as replacement for @@ -62,9 +62,9 @@ # built documents. # # The short X.Y version. -version = '2.0.1' +version = '2.3.2' # The full version, including alpha/beta/rc tags. -release = '2.0.1' +release = '2.3.2' # The language for content autogenerated by Sphinx. Refer to documentation # for a list of supported languages. @@ -156,6 +156,6 @@ # dir menu entry, description, category) texinfo_documents = [ (master_doc, 'AlphaPy', 'AlphaPy Documentation', - author, 'AlphaPy', 'One line description of project.', + author, 'AlphaPy', 'AutoML for Stocks and Sports', 'Miscellaneous'), ] diff --git a/environment.yml b/environment.yml index e7c1153..013651c 100644 --- a/environment.yml +++ b/environment.yml @@ -8,7 +8,7 @@ dependencies: - ipython>=3.2.3 - matplotlib>=2.0.0 - numpy>=1.9.1 -- pandas>=0.19.0 +- pandas>=0.22.0 - pyyaml>=3.12 - scikit-learn>=0.17.1 - scipy>=0.18.1 diff --git a/setup.py b/setup.py index 4bf40b9..e77b108 100644 --- a/setup.py +++ b/setup.py @@ -10,7 +10,7 @@ MAINTAINER_EMAIL = 'scottfree.analytics@scottfreellc.com' URL = "https://github.com/ScottFreeLLC/AlphaPy" LICENSE = "Apache License, Version 2" -VERSION = "2.3.1" +VERSION = "2.3.2" classifiers = ['Development Status :: 4 - Beta', 'Programming Language :: Python', @@ -30,7 +30,7 @@ 'ipython>=3.2.3', 'matplotlib>=2.0.0', 'numpy>=1.12', - 'pandas>=0.19,<=0.22', + 'pandas>=0.22', 'pandas-datareader>=0.6', 'pyfolio>=0.8', 'pyyaml>=3.12', From cdd41d6a5ad7a2719615f5eaedee9d71eab1455d Mon Sep 17 00:00:00 2001 From: Mark Conway Date: Sun, 9 Sep 2018 19:21:48 -0400 Subject: [PATCH 040/129] Change Twitter Account Change Twitter Account --- README.rst | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/README.rst b/README.rst index 0ed9fc0..e2b78ef 100644 --- a/README.rst +++ b/README.rst @@ -71,7 +71,7 @@ http://github.com/ScottFreeLLC/AlphaPy/issues Follow us on Twitter: -https://twitter.com/scottfreellc?lang=en +https://twitter.com/_AlphaPy_?lang=en Donations --------- @@ -83,4 +83,4 @@ http://alphapy.readthedocs.io/en/latest/introduction/support.html#donations .. |badge_pypi| image:: https://badge.fury.io/py/alphapy.svg .. |badge_build| image:: https://travis-ci.org/ScottFreeLLC/AlphaPy.svg?branch=master -.. |badge_docs| image:: https://readthedocs.org/projects/alphapy/badge/?version=latest \ No newline at end of file +.. |badge_docs| image:: https://readthedocs.org/projects/alphapy/badge/?version=latest From eec5ba3919152c161e4f8acd3aa25dab0b89c6cf Mon Sep 17 00:00:00 2001 From: Mark Conway Date: Wed, 12 Sep 2018 19:17:25 -0400 Subject: [PATCH 041/129] Update README.rst --- README.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.rst b/README.rst index e2b78ef..937bd81 100644 --- a/README.rst +++ b/README.rst @@ -82,5 +82,5 @@ http://alphapy.readthedocs.io/en/latest/introduction/support.html#donations .. |badge_pypi| image:: https://badge.fury.io/py/alphapy.svg -.. |badge_build| image:: https://travis-ci.org/ScottFreeLLC/AlphaPy.svg?branch=master +.. |badge_build| image:: https://travis-ci.org/ScottfreeLLC/AlphaPy.svg?branch=master .. |badge_docs| image:: https://readthedocs.org/projects/alphapy/badge/?version=latest From 8ecbf707e0b00185808818108a34021e4e61d3d1 Mon Sep 17 00:00:00 2001 From: Mark Conway Date: Sun, 16 Sep 2018 10:16:33 -0400 Subject: [PATCH 042/129] Add support for IEX The IEX is now the default for end-of-day stock data. --- alphapy/examples/Trading Model/config/market.yml | 2 +- alphapy/examples/Trading System/config/market.yml | 2 +- alphapy/globals.py | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/alphapy/examples/Trading Model/config/market.yml b/alphapy/examples/Trading Model/config/market.yml index 39d392d..ba0ecd2 100644 --- a/alphapy/examples/Trading Model/config/market.yml +++ b/alphapy/examples/Trading Model/config/market.yml @@ -7,7 +7,7 @@ market: lag_period : 1 leaders : ['gap', 'gapbadown', 'gapbaup', 'gapdown', 'gapup'] predict_history : 100 - schema : quandl_wiki + schema : iex subject : stock target_group : test diff --git a/alphapy/examples/Trading System/config/market.yml b/alphapy/examples/Trading System/config/market.yml index 002087d..dbdcd64 100644 --- a/alphapy/examples/Trading System/config/market.yml +++ b/alphapy/examples/Trading System/config/market.yml @@ -7,7 +7,7 @@ market: lag_period : 1 leaders : [] predict_history : 50 - schema : quandl_wiki + schema : iex subject : stock target_group : faang diff --git a/alphapy/globals.py b/alphapy/globals.py index 5b0a71b..3eb23d3 100644 --- a/alphapy/globals.py +++ b/alphapy/globals.py @@ -78,7 +78,7 @@ # Pandas Web Reader Feeds # -PD_WEB_DATA_FEEDS = ['google', 'quandl', 'yahoo'] +PD_WEB_DATA_FEEDS = ['google', 'iex', 'quandl', 'yahoo'] # From f298177f2003b274f604c2c82b7cc9c88461952d Mon Sep 17 00:00:00 2001 From: Mark Conway Date: Sun, 16 Sep 2018 10:17:05 -0400 Subject: [PATCH 043/129] update README.rst Scottfree lower case --- README.rst | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/README.rst b/README.rst index 0ed9fc0..8c823ba 100644 --- a/README.rst +++ b/README.rst @@ -67,7 +67,7 @@ Support The official channel for support is to open an issue on Github. -http://github.com/ScottFreeLLC/AlphaPy/issues +http://github.com/ScottfreeLLC/AlphaPy/issues Follow us on Twitter: @@ -82,5 +82,5 @@ http://alphapy.readthedocs.io/en/latest/introduction/support.html#donations .. |badge_pypi| image:: https://badge.fury.io/py/alphapy.svg -.. |badge_build| image:: https://travis-ci.org/ScottFreeLLC/AlphaPy.svg?branch=master +.. |badge_build| image:: https://travis-ci.org/ScottfreeLLC/AlphaPy.svg?branch=master .. |badge_docs| image:: https://readthedocs.org/projects/alphapy/badge/?version=latest \ No newline at end of file From 806a505c672dc853f99cc525c8f5396e7eef3ed0 Mon Sep 17 00:00:00 2001 From: Mark Conway Date: Sun, 16 Sep 2018 10:18:29 -0400 Subject: [PATCH 044/129] Update README.rst --- README.rst | 4 ---- 1 file changed, 4 deletions(-) diff --git a/README.rst b/README.rst index d6e9a35..9371734 100644 --- a/README.rst +++ b/README.rst @@ -83,8 +83,4 @@ http://alphapy.readthedocs.io/en/latest/introduction/support.html#donations .. |badge_pypi| image:: https://badge.fury.io/py/alphapy.svg .. |badge_build| image:: https://travis-ci.org/ScottfreeLLC/AlphaPy.svg?branch=master -<<<<<<< HEAD .. |badge_docs| image:: https://readthedocs.org/projects/alphapy/badge/?version=latest -======= -.. |badge_docs| image:: https://readthedocs.org/projects/alphapy/badge/?version=latest ->>>>>>> eec5ba3919152c161e4f8acd3aa25dab0b89c6cf From cb6b1c09a32769c88d1beeae83a2dd4922f780a8 Mon Sep 17 00:00:00 2001 From: Mark Conway Date: Sun, 16 Sep 2018 10:20:00 -0400 Subject: [PATCH 045/129] warnings filter warnings filter --- alphapy/__main__.py | 6 +++--- alphapy/market_flow.py | 18 ++++++++---------- alphapy/sport_flow.py | 10 ++++++---- 3 files changed, 17 insertions(+), 17 deletions(-) diff --git a/alphapy/__main__.py b/alphapy/__main__.py index 918b20e..aaa6f50 100644 --- a/alphapy/__main__.py +++ b/alphapy/__main__.py @@ -63,11 +63,13 @@ import argparse from datetime import datetime import logging -import multiprocessing as mp import numpy as np import os import pandas as pd +import sys import warnings +warnings.simplefilter(action='ignore', category=DeprecationWarning) +warnings.simplefilter(action='ignore', category=FutureWarning) # @@ -500,6 +502,4 @@ def main(args=None): # if __name__ == "__main__": - warnings.filterwarnings(action='ignore', category=DeprecationWarning) - mp.set_start_method('forkserver') main() diff --git a/alphapy/market_flow.py b/alphapy/market_flow.py index b062f5d..7aad58e 100644 --- a/alphapy/market_flow.py +++ b/alphapy/market_flow.py @@ -46,10 +46,12 @@ import argparse import datetime import logging -import multiprocessing as mp import os import pandas as pd +import sys import warnings +warnings.simplefilter(action='ignore', category=DeprecationWarning) +warnings.simplefilter(action='ignore', category=FutureWarning) import yaml @@ -265,13 +267,11 @@ def market_pipeline(model, market_specs): # predict_history resets to the actual history obtained. lookback = predict_history if predict_mode else data_history - new_history = get_market_data(model, group, lookback, - data_fractal, intraday) - if new_history < data_history: - logger.info("Maximum Data History is %d, not %d", - new_history, data_history) - if new_history == 0: - raise ValueError("Could not get market data from source") + npoints = get_market_data(model, group, lookback, data_fractal, intraday) + if npoints > 0: + logger.info("Number of Data Points: %d", npoints) + else: + raise ValueError("Could not get market data from source") # Run an analysis to create the model @@ -429,6 +429,4 @@ def main(args=None): # if __name__ == "__main__": - warnings.filterwarnings(action='ignore', category=DeprecationWarning) - mp.set_start_method('forkserver') main() diff --git a/alphapy/sport_flow.py b/alphapy/sport_flow.py index 2bf823d..c35a464 100644 --- a/alphapy/sport_flow.py +++ b/alphapy/sport_flow.py @@ -45,11 +45,13 @@ from itertools import groupby import logging import math -import multiprocessing as mp import numpy as np import os import pandas as pd +import sys import warnings +warnings.simplefilter(action='ignore', category=DeprecationWarning) +warnings.simplefilter(action='ignore', category=FutureWarning) import yaml @@ -792,6 +794,9 @@ def main(args=None): gf['away.score'] = np.random.randint(points_min, points_max, total_games) gf['total_points'] = gf['home.score'] + gf['away.score'] + # gf['line_delta'] = gf['line'] - gf['line_open'] + # gf['over_under_delta'] = gf['over_under'] - gf['over_under_open'] + gf = add_features(gf, game_dict, gf.shape[0]) for index, row in gf.iterrows(): gf['point_margin_game'].at[index] = get_point_margin(row, 'home.score', 'away.score') @@ -855,7 +860,6 @@ def main(args=None): mpos = np.where((mf[away_team] == key_team) & (mf['date'] == key_date))[0][0] except: raise IndexError("Team/Date Key not found in Model Frame") - # print team, gindex, mpos # insert team data into model row mf = insert_model_data(mf, mpos, mdict, tf, index, team1_prefix if at_home else team2_prefix) @@ -913,6 +917,4 @@ def main(args=None): # if __name__ == "__main__": - warnings.filterwarnings(action='ignore', category=DeprecationWarning) - mp.set_start_method('forkserver') main() From 3788798feeb18e8ce80c83ee89633a4c16be73d1 Mon Sep 17 00:00:00 2001 From: Mark Conway Date: Sun, 16 Sep 2018 10:24:10 -0400 Subject: [PATCH 046/129] rewrite sequence_frame remove extraneous exclude_cols parameter (same as leaders); reorder function arguments; and revert to single target --- alphapy/analysis.py | 4 ++-- alphapy/frame.py | 30 +++++++++--------------------- 2 files changed, 11 insertions(+), 23 deletions(-) diff --git a/alphapy/analysis.py b/alphapy/analysis.py index 271acb0..367205d 100644 --- a/alphapy/analysis.py +++ b/alphapy/analysis.py @@ -211,6 +211,7 @@ def run_analysis(analysis, lag_period, forecast_period, leaders, # Subset each individual frame and add to the master frame + leaders.extend([TAG_ID]) for df in data_frames: try: tag = df[TAG_ID].unique()[0] @@ -220,8 +221,7 @@ def run_analysis(analysis, lag_period, forecast_period, leaders, last_date = df.index[-1] logger.info("Analyzing %s from %s to %s", tag, first_date, last_date) # sequence leaders, laggards, and target(s) - df = sequence_frame(df, target, leaders, lag_period, forecast_period, - exclude_cols=[TAG_ID]) + df = sequence_frame(df, target, forecast_period, leaders, lag_period) # get frame subsets if predict_mode: new_predict = df.loc[(df.index >= split_date) & (df.index <= last_date)] diff --git a/alphapy/frame.py b/alphapy/frame.py index 129241c..564a114 100644 --- a/alphapy/frame.py +++ b/alphapy/frame.py @@ -315,9 +315,8 @@ def dump_frames(group, directory, extension, separator): # Function sequence_frame # -def sequence_frame(df, target, leaders, lag_period=1, - forecast_period=1, exclude_cols=[]): - r"""Run an analysis for a given model and group. +def sequence_frame(df, target, forecast_period=1, leaders=[], lag_period=1): + r"""Create sequences of lagging and leading values. Parameters ---------- @@ -325,12 +324,12 @@ def sequence_frame(df, target, leaders, lag_period=1, The original dataframe. target : str The target variable for prediction. + forecast_period : int + The period for forecasting the target of the analysis. leaders : list The features that are contemporaneous with the target. lag_period : int The number of lagged rows for prediction. - forecast_period : int - The period for forecasting the target of the analysis. Returns ------- @@ -339,32 +338,21 @@ def sequence_frame(df, target, leaders, lag_period=1, """ - # Set Leading and Lagging Columns + # Set Leaders and Laggards le_cols = sorted(leaders) le_len = len(le_cols) df_cols = sorted(list(set(df.columns) - set(le_cols))) - for c in exclude_cols: - df_cols.remove(c) df_len = len(df_cols) - # Excluded Columns + # Add lagged columns new_cols, new_names = list(), list() - for c in exclude_cols: - new_cols.append(pd.DataFrame(df[c])) - new_names.append(c) - - # Lag Features for i in range(lag_period, 0, -1): new_cols.append(df[df_cols].shift(i)) new_names += ['%s[%d]' % (df_cols[j], i) for j in range(df_len)] - # Lag Leaders - for i in range(lag_period-1, -1, -1): - new_cols.append(df[le_cols].shift(i)) - if i == 0: - new_names += [le_cols[j] for j in range(le_len)] - else: - new_names += ['%s[%d]' % (le_cols[j], i) for j in range(le_len)] + # Preserve leader columns + new_cols.append(df[le_cols]) + new_names += [le_cols[j] for j in range(le_len)] # Forecast Target(s) new_cols.append(pd.DataFrame(df[target].shift(1-forecast_period))) From 64b5e5b6ea6c164a0772b58b3ae1a7991aec56c1 Mon Sep 17 00:00:00 2001 From: Mark Conway Date: Sun, 16 Sep 2018 10:26:12 -0400 Subject: [PATCH 047/129] fix some market data bugs clean up market data code to accommodate all currently supported feeds --- alphapy/data.py | 15 +++++++++++---- 1 file changed, 11 insertions(+), 4 deletions(-) diff --git a/alphapy/data.py b/alphapy/data.py index d9e0bb7..bfcaa02 100644 --- a/alphapy/data.py +++ b/alphapy/data.py @@ -314,6 +314,7 @@ def convert_data(df, index_column, intraday_data): # Create the time/date index if not already done if not isinstance(df.index, pd.DatetimeIndex): + df.reset_index(inplace=True) if intraday_data: dt_column = df['date'] + ' ' + df['time'] else: @@ -473,8 +474,11 @@ def get_pandas_data(schema, symbol, lookback_period): # Quandl is a special case with subfeeds. if 'quandl' in schema: - schema, symbol_prefix = schema.split(USEP) - symbol = SSEP.join([symbol_prefix, symbol]).upper() + try: + schema, symbol_prefix = schema.split(USEP) + symbol = SSEP.join([symbol_prefix, symbol]) + except: + logger.info("Quandl schema format must be: quandl_DB. Ex: quandl_wiki") # Calculate the start and end date. @@ -485,7 +489,7 @@ def get_pandas_data(schema, symbol, lookback_period): df = None try: - df = web.DataReader(symbol, schema, start, end) + df = web.DataReader(symbol.upper(), schema, start, end) except: logger.info("Could not retrieve data for: %s", symbol) @@ -549,6 +553,9 @@ def get_market_data(model, group, lookback_period, pandas_data = any(substring in schema for substring in PD_WEB_DATA_FEEDS) n_periods = 0 resample_data = True if fractal != data_fractal else False + df = None + to_date = pd.to_datetime('today') + from_date = to_date - pd.to_timedelta(lookback_period, unit='d') for item in group.members: logger.info("Getting %s data for last %d days", item, lookback_period) @@ -568,7 +575,7 @@ def get_market_data(model, group, lookback_period, logger.error("Unsupported Data Source: %s", schema) # Now that we have content, standardize the data if df is not None and not df.empty: - logger.info("Rows: %d", len(df)) + logger.info("%d data points from %s to %s", len(df), from_date, to_date) # convert data to canonical form df = convert_data(df, index_column, intraday_data) # resample data and forward fill any NA values From b1267884b8b103cdaad30dd28768e0523a1cf8b6 Mon Sep 17 00:00:00 2001 From: Mark Conway Date: Sun, 16 Sep 2018 10:26:45 -0400 Subject: [PATCH 048/129] update trading system notebook update trading system notebook --- .../Trading System/A Trading System.ipynb | 384 ++---------------- 1 file changed, 29 insertions(+), 355 deletions(-) diff --git a/alphapy/examples/Trading System/A Trading System.ipynb b/alphapy/examples/Trading System/A Trading System.ipynb index 67ea416..dd16923 100644 --- a/alphapy/examples/Trading System/A Trading System.ipynb +++ b/alphapy/examples/Trading System/A Trading System.ipynb @@ -2,7 +2,7 @@ "cells": [ { "cell_type": "code", - "execution_count": 1, + "execution_count": null, "metadata": {}, "outputs": [], "source": [ @@ -13,62 +13,34 @@ }, { "cell_type": "code", - "execution_count": 2, + "execution_count": null, "metadata": {}, - "outputs": [ - { - "data": { - "text/plain": [ - "'/Users/markconway/Projects/AlphaPy/alphapy/examples/Trading System'" - ] - }, - "execution_count": 2, - "metadata": {}, - "output_type": "execute_result" - } - ], + "outputs": [], "source": [ "pwd" ] }, { "cell_type": "code", - "execution_count": 3, + "execution_count": null, "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "/Users/markconway/Projects/AlphaPy/alphapy/examples/Trading System/systems\n" - ] - } - ], + "outputs": [], "source": [ "cd systems" ] }, { "cell_type": "code", - "execution_count": 4, + "execution_count": null, "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "faang_closer_positions_1d.csv faang_closer_trades_1d.csv\r\n", - "faang_closer_returns_1d.csv faang_closer_transactions_1d.csv\r\n" - ] - } - ], + "outputs": [], "source": [ "ls" ] }, { "cell_type": "code", - "execution_count": 5, + "execution_count": null, "metadata": {}, "outputs": [], "source": [ @@ -78,388 +50,90 @@ }, { "cell_type": "code", - "execution_count": 6, + "execution_count": null, "metadata": {}, - "outputs": [ - { - "data": { - "text/plain": [ - "" - ] - }, - "execution_count": 6, - "metadata": {}, - "output_type": "execute_result" - }, - { - "data": { - "image/png": "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\n", - "text/plain": [ - "" - ] - }, - "metadata": {}, - "output_type": "display_data" - } - ], + "outputs": [], "source": [ "pf.plot_monthly_returns_heatmap(df)" ] }, { "cell_type": "code", - "execution_count": 7, + "execution_count": null, "metadata": {}, - "outputs": [ - { - "name": "stderr", - "output_type": "stream", - "text": [ - "/Users/markconway/anaconda/lib/python3.6/site-packages/numpy/core/fromnumeric.py:57: FutureWarning: 'argmin' is deprecated. Use 'idxmin' instead. The behavior of 'argmin' will be corrected to return the positional minimum in the future. Use 'series.values.argmin' to get the position of the minimum now.\n", - " return getattr(obj, method)(*args, **kwds)\n" - ] - }, - { - "data": { - "text/plain": [ - "" - ] - }, - "execution_count": 7, - "metadata": {}, - "output_type": "execute_result" - }, - { - "data": { - "image/png": "iVBORw0KGgoAAAANSUhEUgAAAYsAAAEUCAYAAADa0BodAAAABHNCSVQICAgIfAhkiAAAAAlwSFlzAAALEgAACxIB0t1+/AAAADl0RVh0U29mdHdhcmUAbWF0cGxvdGxpYiB2ZXJzaW9uIDIuMS4xLCBodHRwOi8vbWF0cGxvdGxpYi5vcmcvAOZPmwAAIABJREFUeJzs3Xl8XHW5+PHPM0sy2feu6U5bKFspoWXfFCgoICoK6hUXRFH0qj+9XtwQEK/XHQUvF7wFFwTFhUVARHYQShdKaUtbutGmaZq02ZfJbM/vj3OSTNIkM0kzmSzP+/WaV2fO+j3pzHnOdxdVxRhjjBmIJ90JMMYYM/pZsDDGGJOQBQtjjDEJWbAwxhiTkAULY4wxCVmwMMYYk5AFCzMhiMiRIhKZKOdNFRFZICINQ9x3uYhsG+40mZFhwcIcFhFpiXvFRKQ97vOHh/lc94tIR9zxh3TTMkOnqltVtTDd6TAjz4KFOSyqmtv5AnYDF8ctuzcFp7w57vjDctMSEY+I2G8hARHxpTsNJn3sB2JSSkSyROR2EdknIpUi8kMR8bvrlovINhG5UUTqRGSHiFw+TOf1icitInLQLfo4r9f6V0TkJhFZCbQB00Tk0yKyWUSa3XR9Im77lSLyLvf9O0VEReRc9/O7ReSVJM87U0Qec693q4hc5S7PE5GgiOS7n7/r5qKy3M8/EpHvu+/vF5GficgTblpfEpFZ/fwdjhSRiIh8xv0/qBKRz8et94rIt9y//QERuVdECnvt+ykR2QM81rtYrb/rcdfluMdrEJE3gBN6pe1bbpqaRORNETkjmf9bkx4WLEyq3QgcBxwLnAicDfxH3PrZQAYwBbgG+LWIzBngeF9yb8SrReSSAba7DjjXPe8pwAf72OYjwEeBPKAa2AdcCOQDnwFuF5Gj3W2fc9MOcCawAzgr7vNzSZ73AWALMBX4EPBTETlNVZuB9UDnDfNMoBI4uY9z4O57PVDspvvGAf4WXjctc4F3ATeKyOnuuq8C5wOnA+VAGPhpr32XAQuBS/s4dp/X4667Bef/dTZwCfCxzp1E5Hjg48BioMBNV+UA12DSTVXtZa9heQG7gHf2WrYXODfu86XAZvf9ciAIBOLWPwx8tZ/jnwgUAX73OC1ART/b/gv4WNznS4BI3OdXgK8nuJ6/A592378LeNV9/yxwNfCs+3klcFGi8wLz3evNilv/U+AO9/0PgR8AmTgB4CvAd3CCWRDId7e7H7gt7hjvBdb1cw1HAgrMjlv2c+B29/1O4LS4dXNwcloSt++0XsdL9nqqgLPj1n0B2Oa+P9q9xnMAX7q/u/ZK/LKchUkZERGcJ8u34xa/DUyP+1yrqsFe66f1dTxVXaOq9aoaVtWHgD8Bl/Vz+mnAnl7H7S1+PSJyiYi86hapNODkEErd1S8Cx4tIKc5T9q+Bhe7n4931ic47zb3e9l7rO/8enbmXZcBq4Gmc3MtpwBuq2hS3X3Xc+zYgt4/r6+9a38YpdhNgBk7xUoN7za/hlDiUuNvGVLWqn2P2ez3usSf3cV4AVHUj8J84uY8at7hqcoJrMGlkwcKkjDqPkNVAfHn6TJzcRqdSEQn0Wt/fzemQU+A8AfdlH86NMP64fe0POOXrOEUqNwOT1Kk8f7rz+KraCGwAvgysUdUwzg39y8CGuBv5QOetAso66yHi1nf+PV7ACTzvwgkc63Ce5M+nZxHUUPROU5X7/9OZ8yuMewVU9YC77UDDUvd7Pe6xa/o4bxdV/bWqnopTPBYAvjukKzMjwoKFSbX7gBtEpEREJgHfAH4Xt94PfEtEMtwK4/OAP/c+iFtxfJlbaep1K5svBx7p57x/xKnfmOo+/f9HP9t1ynLTUgPE3PqQs3tt8xxOnUTnjfvZXp8TnXcbTr3Ed0UkU0SWAFcB90JXQNoIXAs8p6oxnIB0NYcfLG4Qp7HB8cC/AX9wl98BfF9EZgCIyCQRuTjJYw54PTh/i2+ISIFbAf/Zzh1FZJGInCUimUC7+4oe5jWaFLJgYVLt28AmnJvgOuAlnHL5TruACE4OZAXwcVXd0cdxBKcMvwqox3kKvUpVX+7nvLfhPKlvxKlT+ONAiXSfpL+CE3wOAu8BHuu12XM49QfP9/N5wPO6T9sfABa51/sHnPqZF3qdQ4C1cZ9z6C7mGoqom5adOPUwN6lqZ5p/APwTeFpEmnHqXJYkc9AkruebwAGcJtWPAr+J2z0L+LG7fh9OMdq3h3h9ZgSI8/9tzMgTkeU4FbVHpDst45WIHIlTTGZ9JMxhsZyFMcaYhCxYGGOMSciKoYwxxiRkOQtjjDEJWbAwxhiT0LhpIVFaWqqzZ88ekXNpJEzv4jsRQXz+lJ2z7kA9kcj4aobu83kpLi1KdzLGneb6ZmLRWErP4fF6yCvKAyDSHkRjY6s4WzyCLyuQeMMUCja3ocP0/yReD4G87CHtu2bNmgOqWpZou3ETLGbPns3q1atH5Fxt+yrxZGT2WBYLdZA9tTxl5/z5D+9i2vTxNRpC1d79fOGrn0p3Msadv/3qUYonF6f0HHX763j31e8CYP+r6/EP8UaVLuHmNiYvPS6taXjjoZfIKc4flmO11jVx7KWnJd6wDyLS11A4h7BiKGOMMQlZsDDGGJOQBQtjjDEJjZs6i75Eo1Hq6uoIh8PDetxIWzvSHuqxTGNRfFXJDpY6eEcfP5/s7KzEGx4GVSXY3kF9XdOYq7A0xqTWuA4WdXV1BAIBSktLcYbXHx7h5ibE6+2xTKNR/HnDU1nVlz0795GXn2jKgsOjquTmOTNm1h1oTOm5jDFjS8qKoURkhYjUiMiGAbY5W0TWichGEXkubvlyEdkizjzI/znUNITDYXJzc4c1UIxnIoLX4yOQlZl4Y2PMhJLKOot7cKbN7JM7KfwvgUtU9WicuQkQES9wO85cyIuAK0Vk0VATYYFicETE/mbGpFg4GiM2xoZaSlmwcMfLrxtgkw8Bf1HV3e72Ne7ypTjz9O5Q1RDOfMN9TRQ/Jni9XhYvXswxxxzD5ZdfTltb26D2/973vtfj8+/u/Q0XX3oR//GfX+l3n78+9Be++72bAPjDH+/noYcfHHzCjTEpsamqifnfeJxbtrSmOymDks7WUAuAIhF5VkTWiMhH3eXT6TlvbyU952weU7Kysli3bh0bNmwgIyODO+64I6n9VJVYLHZIsLj/D/fxP7ffyQ++/6OkjvPBD1zBpZe8Z9DpNsYMv5e3H+RH/9gCwI7W5EdkCEWVPS3D21BnsNJZwe0DTgTegTNr1ssi8gp9z6ncZ35NRK4BrgGYObOvKZZHlzPOOIP169cD8JOf/IQVK1YAcPXVV/PFL36RXbt2ceGFF3LOOefw8ssvs3jxYtrb21m8eDGTJ02lsLCAPZWVXPeFa7nssvdx6SXv4Vvf/gaVlXsIBLL4zg03sXDBwh7nvP2XvyA7O5uPf+yTvLn5TW66+TsEg+3MmDGTm2+6hYL8ghH/OxgzEW2raebKu17p+uwbRHHvzzbU8ffKVh4+v5xcf3qe8dMZLCqBA6raCrSKyPM4k9VX0nOS93KcqTQPoap3AncCVFRUDFgAeOMjG9lU1TQc6UajURBYNCWXb14wP6l9IpEIjz/+OMuXL2fNmjXcfffdrFy5ElVl2bJlnHXWWRQVFbFlyxbuvvtufvnLXwLwwAMPsG7dOla+tIa8/FxefOkF7v6/31BUVMQt/3UzRx15FL+49XZeWfkK13/ja/zlgf6LnL7+ja/x9eu/yUkVS/nF7T/nl/9zO9d/7evD8jcxxgxsc3UzAHd85ET+samaZ99Ivqn9C9VO8XVVW4QFBRkpSV8i6SyGegg4Q0R8IpINLAPeBFYB80VkjohkAFcAD6cxnYelM2dQUVHBzJkz+eQnP8mLL77IZZddRk5ODrm5ubz3ve/lhRecaYtnzZrFySefnNSx1762losvdqpzTl52Mo0NDTQ3N/e5bXNzM03NzZxUsRSASy95D2vWjMxYWsYY2FbTggicvbCM/ICfjiGMIVjVmr6iqJTlLETkPuBsoFREKoEbAD+Aqt6hqm+KyN+B9UAM+JWqbnD3vQ54AvACK1R14+Gm54aLjz7cQ3Tpq59FfzrrLOINNOFUTk5O0uno6zjWksmY0aehLcTtz2yjvCiLgN9Lpt9DKMmOry3hGFF306q2SApTObBUtoa6UlWnqqpfVctV9f/cIHFH3DY/VNVFqnqMqv4sbvljqrpAVeep6i2pSmO6nHnmmTz44IO0tbXR2trKX//6V84444w+t/X7/f32QK84sYK/PfoIAK+uWklhURG5uX133MvLyyM/P78rN/HIIw9RUXHSMFyNMSaRe1fuJhxVTp1bCkDA5yWsEE3QfHZTfQeX/qOSoBst9rSmL1iM6x7co9WSJUv42Mc+xtKlTpHQ1VdfzQknnMCuXbsO2faaa67huOOOY+aM2fz0xz/rse6z117HN7/1dS573yUEAll877vfH/C83/vu97squMvLZ/Ddm7834PbGmOHx7JYaFk3N57/f7wyLHvA7JROhqJLl6780YHNDCAWuW1TEqtp2Xqxu43OLitJSyT1u5uCuqKjQ3vNZVFVVMW3atGE/VzqG++is4B4JjY0N7N29P+XnsfksUsPms0hsuOazUFWe2VLD0jkl5Gb2/ewdjSkLvvk4V58+h+svOgqAu1/ayY2PbOKv502nIKP/Iu1bN9Txz71OK6jNjSGue2k/hZkeJgV8ZHiETyws4OmqNq6a4qXivacP6RpEZI2qViTaznIWxhgzRC+8dYBP3OM8pP70g8dz2QmHToBW3xYiGlOmFnTPzJfl5iw6ogM/rO9pCTMj14+IcFRhJj9cNolH97TQHIqx+kCQL73i9GU+OpBNwrv9YbJgYYwxQ/DA6j38YZXTf7gsL5Ov/ekNTp1XyuT8ntO11rU6I1QX53aPuRZIIlioKm+3RDihtHu/JaUBlpQGUFUu/kclbRFn/6Zw6kuIbD4LY4wZpJaOCF/903rW7K7no6fMYsVVJxGKxli1q45/btrP+soG5n39Mf64eg8HW5xgUZLT3T8i4NY5DNQial97lIMdURYVHjqwp4gwI8ff9bkymNo512EC5CxU1ZqTDoKqDti01xgDVQ3tANx6xQlccvw0wtEYmT4Pj67fx+Mbqru2u/Hhjfzg/ccDUJLbHSwyE+Qs/m9zA49XtgCwuCTQ5zYzcnxsaXQC0d725IcOGapxHSz8fj8tLS02THmSVJVoLEKwvSPdSTFmVNvrBovphc6N3O/1cHx5YY9AAdAailLX6vyeiuNzFj4nWGxpDLGoqDvn0BSKsvZgB/dud0abKMrwMCu379v0BTNyaYnEaArFmDwCswqM62BRXFxMXV1dv72ahyrS1op4erWGikXxNbcM63nitbQ2E9PUtrGOnynPGNO32uYOPn73KgCmFXbPXvnDy49j7e56/F4P1/3+ta7l22qc+0JR9qHFUL/YWM/y8hyyfM7n2zbW88+q7pGpSwLefh90TywNcGKpE6xaR+A3O66DhdfrpaysbNiP27avEk9Gz1AeC3WQPXX4m+l2+tO9jzJt+uSUHd8Yk5xfPrut6/2kvO4iolklOcwqySEWU6A7WPz65bcBJ/fRqbOCG5xe2fPyM2iPxHhxfzvvnJbNeeU5fO3VWs6dlvyIDqk2roOFMcYMp7+sreTul3Z1ffZ6Dn3q98Qt+8H7jqOuLcTk/J4Pl/HBYm+rEyy2NIYIRpV3Ts/hpLIs7j1nGlOykhtWaCRYsDDGmCS9vP0gAP/40plMzuu74jneB06a0edyv7c7oHSO91TpDuUxM9dp5TQ1e3TdnkdXaowxZhTbsr+Z044oYcHkvAG3e/JLZ9LY3v8IsdMLs/jozAC/2R3kV5sb2FTfwfQcH34PlI2i3EQ8CxbGGJNAS0eEX72wgy3VzXxoWeKJ1uYnCCYiwuXTAxxRlstv32pkQ30HCkzL9uEdpS03rVOeMcYk8Mr2g/zsn2/h9QhnLhi+RjOnT8lm6aQsmsIxKlsjTI/raDfaWM7CGGMS6Ig4PaQf/NxpCYugBqsgw0NM4e2WMEvLEteDpIvlLIwxJoFw1AkW8c1fh0t+XMuo8pzR+/xuwcIYYxIIdQWL4a9PKMjovg2P5mIoCxbGGJNAZ84iIxU5i7hgMcNyFsYYM3aFI6kshuo+ZmlgdDabBQsWxhiTUNgdHdbvG/5bZudMeYUZnlE94OnozfMYY8wokco6i1y/hy8fW0xF6ehtCQUWLIwxJqGu1lCe1BTGvHtmbkqOO5ysGMoYYxIIR2P4PNJjkMCJxoKFMcYkEI5qSiq3x5KUXb2IrBCRGhHZ0M/6s0WkUUTWua9vx61bLiJbRGSbiPxnqtJojDHJCEViKamvGEtSGSrvAZYn2OYFVV3svm4CEBEvcDtwIbAIuFJEFqUwncYYM6BwNEZGClpCjSUpu3pVfR6oG8KuS4FtqrpDVUPA/cClw5o4Y4zpJRZTPn/fa3zorlfoiER7rHNyFhYs0ukUEXldRB4XkaPdZdOBPXHbVLrLDiEi14jIahFZXVtbm+q0GmPGsarGdh55vYp/bT/I1uqWHuvCUQsW6bz6tcAsVT0e+AXwoLu8r4JB7esAqnqnqlaoakUq5to2xkwcuw60db1/s7qpxzqngtvqLNJCVZtUtcV9/xjgF5FSnJxE/FyE5UBVGpJojJlAdh7ozk28ua9nsAhZziJ9nfJEZAqwX1VVRJbiBK6DQAMwX0TmAHuBK4APpSudxpjx7+HXq/jTmkqy/F7mTcphW82hxVATvYI7ZcFCRO4DzgZKRaQSuAHwA6jqHcD7gWtFJAK0A1eoqgIREbkOeALwAitUdWOq0mmMmdhUlW/85Q06ojGWHz2FjkiU7bWtPbaxOosUBgtVvTLB+tuA2/pZ9xjwWCrSZYwx8Srr22nuiHDLZcfw4WWzuPGRjbz41gFUtWtgv3DE6iwmdqg0xkx4W6qbAThySj4A0wqyaA1FaWqPdG1jdRYWLIwxE1gspvxu5dt4PcLCKc7c2tMKswCobOhuHRWOxlIy8dFYYqPOGmMmFFXl6b1tZK/bS2N7mGe31PLl8xaQm+ncDmcWZwPwrp+/yJT8AE986Uyrs8CChTFmgnmrvoPrVx6ElQe7ll1w9JSu98dMz+cXV57A3zdW8+j6feyobXH6WUzw1lAT++qNMRPO9oYQAL/88JKuZXPLcrreiwgXHz+NT585F4BfvbCThrbQhK/gtpyFMWZC2dUYwgO846hJXcv6KmKalOfMXPfoG/sArM4i3QkwxpiRtKsxxLQcH5k+L6u/+U4i0T5HE6I0N6PH5xluXcZEZcHCGDOh7GsNU57j3PpKczP73c7XKyfxoaUzU5qu0W5i56uMMYdFVQn182Q+WrWEYuRmDK7+4fVvn09RTkbiDccxy1kYY4bs1qfe4mf/rOTLFWV88Kgi/GNgjuqWcJQcnz+pbW+4eBExhYLs5LYfzyxnYYwZstue3gbAT1bX8uj2xhE99+aDQf79qUpq2yKJN47TEo6R60/u1vfx0+bwydPnDCV5444FC2PMkHnjchINwegAWw6vmCof+tvbvFDZytr9bYl3cEViSjCi5PhGfw5otLFgYYwZtOoQLP/Z83REYl3L9jSHR+z862vbu97vbgolvV9b2ElvTpI5C9Mt4V9MRP5dRPLF8X8islZEzh+JxBljRqfVzbB1fzPvXTKdm04qZlK2jx0NHSk/76p9bVS1hFm9zwkWeRke3m5KPki1dAYLy1kMWjIV3J9Q1VtF5AKgDPg4cDfwj5SmzBgzKr1eH+Lv9XD8jEJ+8oHF7H91PW8H4f/eqOOU323lw4uKuG7J4U9zHIzEuP21A1RMyaY44KUs28en/7GH2QUZLJ2STX6Gh6NKArxc1cpXn91LOKoEo8q3T53CtNy+K6RbQk5RWbJ1FqZbMsGiMwRfBNytqq9L5yDvxpgJ59G9QQCuOKl79uPPLC6lJMvHbzfW8dr+9h7br6xq5R+7mvlSRRm5Gd6kz7O6uo17N9Vz76b6HssPtEWoD0YpDvi4YE4eNW0RVu1roynk5Br+e+V+bn1HeZ/HbLViqCFL5i+2RkT+gRMsnhCRPCCWYB9jzDgUjCqbmyKcWwhXxnVS83qEK44qYlFpgPpglHPv38Y9Gw7SHo5x7ZOV/PWtRp7c1Tyoc+1sdOoibjljKqdM6+49PTXXT10wQlHAy3vmF/Ln98zhU8eXdK1/obKVfS19F01ZMdTQJZOz+CSwGNihqm0iUoJTFGWMmWBqg1EiCrP66fhclOlll1vh/PM1B1hfE+xa9/TuFi5bUJj0uXY2higOeLlwbj57W8K8XOW0eqoPRojEvMwt7O4k9+55Bazc18a75uZz/fP72HggyFS3KGplVSvffXk/fo9w4Vxnzgorhhq8hMFCVWMish9YJCLWic+YCazDLVPI7OdeWxToeYt4paqVEyZlUZrlY2t9sO+d+rGzoYM5BU5AmJnfXQdxsD1KMKJUTOnObRRkevn5O8rpiMbwCmypC/LO2U5geHp3C1UtYRS4Z0Mdfo9QEki+OMw4Et78ReS/gQ8Cm4DOhtQKPJ/CdBljRqFwzBnaw99PKU5Rr5vw55aU8uFFxdz8r2rawoMbFqS6NcLSqU5AmOz2oPZ5IBJzipN6nwsg0+thTmEGv3+zni11Hfz8neVsrQtyXFmA9bVB2iPK8jl5lrMYgmRyCu8BFqpq6tvFGWNGtQ43WGT0m7PoeQM/a0YuAFk+D+1un4yWUJSmUIypOT76aysTU+VgMEJplnOLOq4swA/PnkbAK3z+qb0AlOf13eLps4tLufP1g7y4t5X9rWHW1wb54JGFvF7r5GwumJM/iCs2nZIJFjsAP2DBwpgJzm1wRH/j8GW7T+wLijK57+JZXcEg2++hLRJDVfnF2gM8sKWB4oCXh947t8+WSY0dUSIxKHGDhYjwjll5qCoPXjaHqCqz8/se2O/smXlUt0bY/GoN33tlPwqcMzOPR7Y30RqOObmV9sEViZnkgkUbsE5EniIuYKjqF1KWKmPMqNQ5wmx/xVALi5ya7y9VlPXINWT7hJg6ranePOjcqOuCUfa3hplbeGht+cF2p8S7NLtnTkVEmNlPkIjXGYC21HWwZHIWJ03N5tcXzWRfS5gsn4eR62s+fiRTcPcwcDPwL2BN3GtAIrJCRGpEZEOC7U4SkaiIvD9u2VUi8pb7uiqJNBpjRkCiYqhJOX7WXrWQZdNyeizPcm/e7eEYtW0RSrOcINDkdpLbWhfsMYTHgXZncMCSwNDa1HTmcGrauouy5hZmclp57pCOZxLkLETEC5ynqh8ZwrHvAW4DfpPg+P8NPBG3rBi4AajAqUhfIyIPq2p930cxxoyURMVQ/cn2OTfvxlCUmrYIp5fn8EJlK41u86qrHttNR1R59oojyM/0dgWLsuyhBYv4Cuxia/k0LAbMWahqFCgTkUHP+qGqzwN1CTb7PPBnoCZu2QXAk6pa5waIJ4Hlgz2/MWb4hRK0hupP55P+T1fXosCRJc781o0dTs6iwy3eemxHEwDVLW6wyDq8nAVA8RBzJ6anZIqhdgEvici3ROTLna/DPbGITAcuA+7otWo6sCfuc6W7zBiTZh1RRYDBdoDuzFm8WNlKple6emQ3ucGiMNN5+n+tximK2t7QwbRcf1fx1WDFV5oXZVnOYjgkE3Kr3JcHyBvGc/8M+JqqRns1n+vra9hnA20RuQa4BmDmzIk9P64xADXNQfaHoDhFxw/HnPqKwY4OF/+k/+KH5uMR8EpnqyftymG8XtOOqrK9oaNHD+3ByrFiqGGXTA/uG1N07grgfjdQlAIXiUgEJydxdtx25cCz/aTtTuBOgIqKirE1EbAxwywaU5be8hQAv5+RYOMh6ogpGR6hn+e3fmXFZUU6J0zKy/DSFIrR0BFFgTkFGexsDLGnOcyuphCnH0ZldI4VQw27ZHpwP0Mf3wxVPfdwTqyqXXMVisg9wN9U9UG3gvt7IlLkrj4fuP5wzmXMeNXQFuLnT22jORjmhbcOpPx8oZiS6R18sOjMWXjjciQFmR621gX54+YGAM6ZmcvON+r41fqDRGKweFLWkNMZn5OZX9TPQFZmUJIJuV+Jex8A3gcknPRWRO7DySGUikglTgsnP4Cq9q6n6KKqdSJyM7DKXXSTqiaqKDdmQnrqzRpWvLSzx7LMFA6oGorCUKoRstw6i6Pcim2A8rwMXtrb2tWzetnUHO57s56/bW8ix+/h5LiRZgfLE9/Hw4b2GBbJFEP17lPxkog8l8R+VyabCFX9WK/PK4AVye5vzERV1eBUCK//zvkc9x1nPrJUzh/QEVMyPYOPRiVZPr5z2hROn97d/+KHZ0+jujXCK1Wt3LX+IPMKM7j59KlsPBjkuLIsMryHf5M/pjSQeCOTlGSKoeLryjzAicCUlKXIGJO0qsYgJTkZ5Af8rPrGO7nrhR3c+fwOYqo9nq6HS6irzmLwLjmioMfngM/D7IIMZhdkcMVRTqnzubPyOHfW8LSjee7KI9wiMzMckimGWoNTQCk4xU87cea4MMakyZ66Nj73+7VUNQSZVuiU7ZflZVKc47QgCsVgOBsBBaPK9uYIlW1RFuSNjQrjvEHMymcSS+Z//ShV7THqlohYjZExI2xPXRsX3foCwUiUcLS7gnnJzO4JhQJu3UAopgSG8an697va+Gd1BwJcND0A7Ta60kSTTKHgv/pY9vJwJ8QYM7Ct+5tp7ohw6eLpXH36nK6+DnPLupuYZrlP0509rYfLrpYIc3O9/GhJAQvz+x4a3Ixv/eYsRGQKTs/pLBE5ge7OcvnA0JspGGOG5ECLM+jzF985n/KibM4/egp/31DNZ8+Z17VNwO8Gi2ifhxgSVWVve5QzJmUy1XpDT1gDFUNdAHwMp1PcT+KWNwFfT2GajDF9qG12gkVprlMKvHROMUvn9Oyr3RUshjFnUROM0R6F6RYoJrR+g4Wq/hr4tYi8T1X/PIJpMsb04UBLiLxMX1dA6EsywUJVqQspPoGC/sYaj/PrnW34BRYVWPHTRJZMncVLIvJ/IvI4gIgsEhFrDWXMCKtt6aBK5utKAAAgAElEQVQsb+C2JVlusOgYoLPFM/s7+PzqBq5d1cCO5oT9a9nVEuG0sgymZ1vOYiJLJljcjTPfxDT381bgiylLkTGmTweaO7qKoPoT8He3hurPjpZo16ixbyUIFjFVmsKaVA7EjG/JNJ0tVdU/isj1AKoaEZFhrD4zxgDsbwpywc+epzkYIRpT/v0d8/nSeQsAp+hod10bFbMHHk82q6uCu/9gUd0eZU6uj33tUXa3DfxTbokoMaDAhsyY8JL5BrSKSAnuyGEicjLQmNJUGTMBrdvTQENbmKVuQLjz+R1d67bXtrKvMcjJcwcOFt11Fn2vX3UwxKamCFOzPMzI9rKnbeCcRVPYOVDBYGc7MuNOMsHiyzjzcM8TkZdwpkn9fEpTZcwEtK2mBYC7rqrgtCNKmFbYPa7RA2uc+cDOnF824DESVXDfvtU5R6Hfw+SAlwPBgUeSagw5x8m3nMWEl2gObg/OSLNnAQtx+lpsUVXrvmnMMLrnpZ388IktAORm+phWkMWO2lbA6V9x1/M7eO+S6cwoHriLU2envAd2txOOub2tXW80hLtyHOdNDfD3qiBtAxRXATR25iyszmLCSzQHdwz4sapGVHWjqm6wQGHM8Hp5+0G+88gmABbPcIbuyMn00drhFBGt2llHTOHDy2YlPFZupo+LisEj8Fp9qMe6/9rYDMB7ygOUZHrI8gmhGEQGqAw/6DarKrRiqAkvmQruf4jI+4C/qKrNRmfMMLt35duU5mZy/zXLyA84fRmyM7y0haL89MmtPPJ6FQG/h2OnFyQ4kuOiYtinPurjKi464nIQZe4Igznu2FH/8Voj3z+hoM/RZDc1Rpga8JBrxVATXrJ1Fg8AHSLSJCLNItKU4nQZM2HsrmvjqKl5HDEpj0n5TrFRTqaPSEy59am3iMSUq0+fS4Yv+Rt2nl+obo+y123t1BAXOLLdINE51Wl1MNa1XbxITHmzKcxxRdYZzyQRLFQ1T1U9qpqhqvnu5/yRSJwxE8HuujZm9qqLyI4bXvveq5fxlQsWDuqYOT4hGIOvvtZIOKY0uHUPs3K8LCl2bv45caPSdvRRFHWwI0YoBrNyxsaQ5Ca17FtgTBpEojF8Xg9NwTANbeFDKq5zMrp/mokqtfuSG5cL2RBXsX3t/Bz8bnFTtq87WDSHDw0Wde5OJZlWBGUsWBgz4l7adoCPrniVOaU5lLiTFc0o6pWzyHRyFkOclI7cuECw6mCIQrc1U3znuixvfLA4tAltZ+V2ibWEMliwMGbE/e/zO4jGlMr6NrIzvJw6r4ST5hT12KYzZxGfwxiMnLhg8WyN0yoqw+PUZfS1TVMfOYuDlrMwcZL6JorI6cB8Vb1bRMqAXFXdmdqkGTM+rdlVx5VLZ3DDxUf3O4JsZ51FTubQgkXvKogzJ2Vw/pRAj3m5s3z95yzW1oVYUxci1yc2j7UBkggWInIDUIHTKe9uwA/8DjgttUkzZvwJhqO0hqKUF2UPONR4dmfOInNoI72WuyPEXjI9wJtNET48O5u8Xs1fs+OCQFOkZ3S5c1srTWHlxGJrCWUcyTy2XAacAKwFUNUqEclLaaqMGacOtjpFQsVuXUV/wjHnST8vMLSb9excH3ctKyRngOa2HhF+f1ox31nfRFVc09moKs1h5b0zArxvRtaQzm/Gn2QKI0NuZ7zOgQRzkjmwiKwQkRoR2dDP+ktFZL2IrBOR1W5RV+e6q0TkLfd1VTLnM2YsqGtxgkVJgmBx7PQC3reknJ99cPGQzzVQoIh3Uomfna1Rfra5mW3NEVrCiuJUhotYEZRxJJOz+KOI/C9QKCKfAj4B3JXEfvcAt+EMPNiXp4CHVVVF5Djgj8CRIlIMdBZ9KbBGRB5W1fokzmnMqHaw1ZkatSR34GDh93r48QeOH4kkccakTFYeCLGmLkymR9jc5AwzYoMHmnjJdMr7EfAn4M849RbfVtVfJLHf80DdAOtb4oYPycHNueDM/f2kqta5AeJJYHmi8xkzFtR1FUMNPInRSMr3e7jp+ALKs728Vh+m1m0ym2/jQZk4yVRwfwl4QFWfHO6Ti8hlwH8Bk4B3uYunA3viNqt0lxkzpoUiMXYdcEaSTVRnkQ7FGR7ebu2uu7AJj0y8ZL4N+cATIvKCiHxORCYP18lV9a+qeiTwHuBmd3FfjzN9DmAoIte49R2ra2trhytZxqTEZ+9dy8+f3kaW30t+YPR1cSrq1fkuP8NyFqZbMsVQN6rq0cDncObhfk5E/jmciXCLrOaJSClOTmJG3OpyoKqf/e5U1QpVrSgrG3hSGGPSbcPeRpbNKea3n1w6KiuOi+OCRY5PeowdZcxg8pk1QDVwEKfY6LCIyBHi/mJEZAmQ4R77CeB8ESkSkSLgfHeZMWNWMByluinIaUeUJpxHO12K3J7a+X7hrmVFozKgmfRJps7iWuCDQBlORfenVHVTEvvdB5wNlIpIJU4LJz+Aqt4BvA/4qIiEgXbgg26Fd52I3Ayscg91k6r2W1FuzFiwu64NgFklgx8UcKQsLvRzRlkGxxZaRzxzqGQKTmcBX1TVdYM5sKpemWD9fwP/3c+6FcCKwZzPmNHs7YOdwSKpbkppUZTp4doFuelOhhml+g0WIpKvqk3AD9zPPfLO9rRvTPK27nemNJ1bNnqDhTEDGShn8Xvg3cAanNZI8QWYCsxNYbqMGTee3LSfHz6xhfKirK5pU40Za/oNFqr6bvffOSOXHGPGny/9wSnBDUUOnTPCmLEiYWsoEXkqmWXGmL51Fj195ORZaU6JMUM3UJ1FAMjGac1URHcxVD5OfwtjTBLaQ1HOWVjG5889It1JMWbIBqqz+DTwRZzAsIbuYNEE3J7idBkzbtS2dHDy3BLrt2DGtIHqLG4FbhWRzyczcKAx5lChSIyGtjCluaNn4EBjhiJhPwtV/YWIHAMsAgJxy/sbetwY4+ockrwsz4KFGduSnVb1bJxg8RhwIfAi/c9TYYxx1TY7waI0wfwVxox2yYwN9X7gHUC1qn4cOB6wxyRjklDV0A7AtEKbntSMbckEi3ZVjQEREcnHGVDQOuQZk4TKeidYzCgavWNCGZOMZILFahEpxJlKdQ2wFng1pakyZhRSVW56ZBPn/uhZmoLhpPaprG8nL9NHftbom7/CmMFIpoL7s+7bO0Tk70C+qq5PbbKMGT3aQhH+sGoPb+xt5C9r9wLw4Gt7+egpsw/Z9q+vVXLLo2/yyvXvYF9jkDf3NTG9KMuazZoxb6BOeUsGWqeqa1OTJGNGD1XlM79by/Nba8n0ebjg6MnsrmvnL2v7Dha3PLqZAy0hvvXQBu5ftQdVuOjYKSOfcGOG2UA5ix8PsE6Bc4c5LcaMOlv2N/P81lq+tvxIrj17HgC3P7ONHz6xhb0N7Tz42l5yM31cdepsAGaXZHOgpYP7Xt3DrJJsvv3uRRw/ozCNV2DM8BioU945I5kQY0aj7TWtAJy5oLRr2SXHT+NH/9jCD/6+mYfWOTP+fmjZTPxeDxm+7mrAdxw5mXccNWxT1huTVsn0s/hoX8utU54Zb8LRGNGYEvB7u5btqG0BYE5p9zwUM4qzuejYqV2BAuChdVW8/8RyDrR0dC0bzbPiGTNYyTTROCnufQCnz8VarFOeGWcuue0lDrZ08Oo33tm1bMeBVqYXZpGd0fOn8uPLj+czZ84jN+Djqw+8zo2PbOSS46dxoCXEqfNKaA5GOP9oy1WY8SOZ1lCfj/8sIgXAb1OWImNG0N0v7eQPq/YQjSlv1bQcsn57bUufs9sF/F6OLS8A4Joz53LNb9fw4rZa6ttCnDS7mC+dtyDlaTdmJCXTz6K3NmD+cCfEmHRY8dJOWjoizJ/cPfd0WygCOC2hdtS2Mrd04KlQTz2iFL9X+MQ9q1GFSfk2wIEZf5Kps3gEp/UTOMFlEfDHVCbKmJFw5Z2vsKeunW+/exGfOH0Of1pTyVceeJ3a5g5mlfioae6gpSPCvEm5Ax4nN9PHnR+tYHtNC36vh4uPt+lezPiTTJ3Fj+LeR4C3VbUyRekxZkRUNbTz8o6DAJy1sAyASe7IsB/+1Uqe+n9nsd2t3J5bOnCwADhn4STOWTgpRak1Jv2SqbN4DsAdF8rnvi9W1boUp82YwxIMR7nv1d185ORZ+L3dJa6bq5tY8eJOAP587SnMK3OCQecw4pX17TyzuYZNVU14BI6cmjfyiTdmlEmmGOoa4GagHYjhzJin2GCCZpS751+7+P7jm/F5PfybO/91fWuID/7vKzS2h5lRnMUJM4q6to+fc+Izv1tLps/DaUeU2sRFxpBcMdRXgaNV9cBgDiwiK4B3AzWqekwf6z8MfM392AJcq6qvu+uWA7cCXuBXqvr9wZzbTEyrd9Wxald9V0/rlqBTUb2voZ1gOMrdL+3i6c37ae2I8NDnTmPRtHw8nu4xm0pyMrj8xHIWTM5jx4EWQhHlo6fMSsu1GDPaJBMstuO0gBqse4Db6L8/xk7gLFWtF5ELgTuBZSLixZnj+zygElglIg+r6qYhpMFMIFfc+QqRmHLhMVM42NrBbc9sA+CXz25nS3UzT22uIT/g44aL+x6CQ0T44eXHj3SyjRkTkgkW1wP/EpGVQFf3VFX9wkA7qerzIjJ7gPX/ivv4ClDuvl8KbFPVHQAicj9wKWDBwvQrFIkRiTmN9r776Cb++WZNj/XPv1XLNWfO5esXHZWO5Bkz5iUTLP4XeBp4A6fOIhU+CTzuvp8O7IlbVwks62sntz7lGoCZM2emKGlmLHhtdz0Ax04v4KnNPQPF/deczMlzS9KRLGPGjWSCRURVv5yqBIjIOTjB4vTORX1spn0sQ1XvxCm+oqKios9tzPgWiyn7m4M8uWk/HoF7P7UMn0dQhXtXvs1RU/MtUBgzDJIJFs+4T/CP0LMY6rCbzorIccCvgAtV9aC7uBKYEbdZOVDVe19jmoNhPvnr1by60/kqLplZSH7A37X+mjPnpStpxow7yQSLD7n/Xh+37LCbzorITOAvwL+p6ta4VauA+SIyB9gLXBGXBmMAZ4TYT/92DWverucr5y+gNDeTpXOK050sY8atZDrlzRnKgUXkPuBsoFREKoEbAL97zDuAbwMlwC/dKScjqlqhqhERuQ54Aqfp7ApV3TiUNJjxpzkYZkt1M++/42UAbrr06D5nrDPGDK+UzWehqlcmWH81cHU/6x4DHkuUNjOxtIeinPeT56luCgLOJEQfWmoNG4wZCTafhRkTmoJhzvrBM9S3hfn6RUdy3qIpPSYkMsakls1nYcaENW/XU98W5t9OnmUV18akgc1nYcaENyobAfiP5QvTnBJjJiabz8KMCW/sbWRuWQ55cU1jjTEjx+azMGPCG5WNLJtrTWONSZd+g4WIHAFM7pzPIm75GSKSqarbU546M2E9s7mGbz+8gYc+dzqRWIzqpiDHTi9Id7KMmbAGyln8DPh6H8vb3XUXpyRFZkLaU9fGF/+wjtd215Od4eOdR01iT1079726myOnOJMPWbAwJn0GquCerarrey9U1dXA7JSlyExINz6yia3VzVy5dCYtHRGe2LgfgN+8vIsHVleSm+njuPJDhxU3xoyMgYJFYIB1WcOdEDOxba5u4pwjJ/GdS47G5xHaw1H8XmF/Uwd/31jNJYunkZXhTXcyjZmwBiqGWiUin1LVu+IXisgngTWpTZaZSILhKHsb2nn/ieX4vR5ml+awraaFpXOKue6c+bSFIjbukzFpNlCw+CLwV3f6087gUAFkAJelOmFm4nj7YBuqdPXInj8pl201LZTlZnLKPBte3JjRoN9goar7gVPd+SY659B+VFWfHpGUmQljW00LAPPKcgE4e2EZj2+opqoxmM5kGWPiJOzBrarPqOov3JcFCjPs1rxdT8DvYcFkp9XT8qOnAvDu46amM1nGmDjJdMozJqVW7apj8YxCMnzOs0tBtp+3brkQv3coo9EYY1LBfo0mrVo6ImysauSk2T0rsC1QGDO62C/SpNW63Q3ElEOChTFmdLFgYdKitgN+3VDGx+5+FY/ACTOtw50xo5nVWYwBz2yu4fVgNtPSnZDDtK5BaAhDlgf2BuFg1M/Vp89m0bR8G03WmFHOgsUY8PF7VgGFnBaOkj9G76mNYfjd7p4Z2Vn+IN9896I0pcgYMxhWDDWGvN4o6U7CkO13u0x8cnaUEwtjeFCOzWxLb6KMMUmznMUoV98a6nq/rz2NCTlMNR1OoJueBQvzlPeVKwf2Wac7Y8YKy1mMYo+/sY9zfvxs1+fqjrGbs9jTBlleJc8HHoEM++YZM6bYT3YUe3ZLLZGocu3Z8zgms5X9QVBNvN9os70F1jR4OCZfkbEb74yZ0FIWLERkhYjUiMiGftYfKSIvi0iHiHyl17rlIrJFRLaJyH+mKo2j3c6DrRw1NY+vLT+SKb4wHTHhYCjxfqPJpib4nx3O0OKXTR+Dkc4YA6Q2Z3EPsHyA9XXAF+g5xzci4gVuBy4EFgFXisiEbDKz80Ars0uckVin+Zwosatt7DyaN4bhD3ucr1jAo1b0ZMwYlrKfr6o+jxMQ+ltfo6qrgHCvVUuBbaq6Q1VDwP3ApalK52j15T+uo7a5gzllTrAo8UYIeJRdrWlOWJI2N8PNb3ppjQpZXuWjs2LpTpIx5jCMxtZQ04E9cZ8rgWVpSktaqCqPvbEPgPMXTQFABGZld+YsRn9xTlW7kwO6ckaM4woUv+UqjBnTRuNPuK9ylj7vjiJyjYisFpHVtbW1KU7WyKmsbycYjvHd9xzDEZNyu5bPylH2B6FmDLQ4rQtBtlc5scgChTHjwWj8GVcCM+I+lwNVfW2oqneqaoWqVpSVlY1I4kbCxqomAI4rL+ixfG6Oogg/2OrloarRXXdRFxKKM9KdCmPMcBmNwWIVMF9E5ohIBnAF8HCa0zSimoJONU5Rds+77bwcuGpWlBMKY7xwwEPDKG4ZVReC4ozRX1xmjElOyuosROQ+4GygVEQqgRsAP4Cq3iEiU4DVQD4QE5EvAotUtUlErgOeALzAClXdmKp0jkbBcBSArAxvj+UicGwB5PqU1xpgXxAKR+HTe0yhPgzHFCTe1hgzNqQsWKjqlQnWV+MUMfW17jHgsVSkayxoCznBIrtXsOg0OdP5d3+HcNQorOxujkBUxXIWxowjo7EYasJrd4NFwNd3sMj2QZ5PuwbnG23q3OIxCxbGjB8WLEahYDhKwO/B4+m/ErssE2rTMFaUKuxtd4qaACrb4MG90vUZnMptgOIxOpy6MeZQFixGobZQlCx/37mKTkUZSkNcd8bKNvjbPknJ2FGqTnCIKfylSvjpW17W1DsB4Te7Pbx40ENNR/f2nTmLolFYn2KMGZrR2ClvwmsPJw4WhX5nOI2oglfglzs8hGLCGaVRCobpiT4Sg5++5WF/h7C0KMbMbHj5oAcPylstcFKxc36At1uFKQHnQ1VQKPJb/wpjxhP7OY9C7aHoIS2heivygyI0u7mLUMx50h/Oeoz6sFOJDvBqvYdnaoWpAeWYAuWtFmF7C4TdUTzeanH+jSlsa4H5uVZfYcx4YsEizaobg3zwf1+mqqF7ZqP2cOJgUehWHteHoTXSvXz/MNRjtEVgVZ3wXK1zrA+UxyjwK21ROKNUObXEef8/O7y0RQUPyhtNTuDaH4T2qDAvN8FJjDFjihVDpUlDW4iCLD+/fHYbK3fW8Ze1lVx37nzAzVkkUQwF0BASAt7up/jhyFk8XSs8W9v9HDEvV/lWcc+cwlcXxKgLORMZeQVu3+5lV1v3WC2TMy1nYcx4YsEiDVbuOMgH73yF8qIsYm4zosb27trqtnCUgqyBKx46g0V9GPLiNm2NHv5AgztbhekBZW9QepwrXmmm8wJod1r6cjAkeN1oYZXbxowvVgyVBqvfrgcgy+8l4BY33fXCTtZXNlDfGqK5PUxWgtrhgNeZprQhrhgqw6N0RA8vbRub4O02YX5ed8DxJijZyvJCjlc50OG0hMrwKNkDZ4yMMWOM5SzSYEt1M9MLs3jyy2cB8PW/vsHvV+7mktte6trm2PLEY2UU+aE+JLQGnM/FGdDRz7QR4Rg8f0CYl6PMzun/mE/ud4LU4gJlSWG0R33IQEoyobJdyPA46bLpU40ZXyxnMcIeXb+Ph1+vYsHk7hrgb1x0FJ87Zx4A7zpuKuAElEQ6m8+2uDf0Yj8E+8lZvHBAeLzaw4NV/f+Xx9Sp8zijNEZ5NkzLgvl5yV3XlEylsl3Y0SpMykxuH2PM2GE5ixF2/6rdAFxe0T0Ke06mj6+cv5BPnDYHn9fDo+v3JayzAKdF1PZWpxVSllfJ9ilVwb4f6Tvn7q5sF3681UNM4aRi5aQiJcf9FtSFIKxO89jBunSaUlHsRKqpgUHvbowZ5SxYjICNVY2s3lXPwZYOVu+q56pTZnHRsVN7bCMilOQ6j+R/vvZUZhZnJzzukXnKvw56WNcIOV4IePovhmqNCNleZV4OxID6EPxtn4cXDyjfPMrZaZ/bkmrKEIJFphfmDlC8ZYwZ2yxYjIB3/fzFrve5mT4u7BUoejtxVlFSxz0yD+ZkK1VBp3lrphc6os7wHL3rDFqjzhP/VbOdwNAchhvf9NIQFkIxyPDAvqAgKJMtZ2CM6cXqLFLgYEsH/37/a9S1hmjp6FlDvOHGCzh5bsmwnMcj8LkjYtxyTIzLy5VMD8QQwn1kDFojdBU3gdPc9iMzncBxwB3XqTroVJJn2rfCGNOL5SyGSUMwQlVNM1MLsvjTmkoeWlfFpLxM3nHU5K5tMn2pvQt33uQ7ok5OIV5rxGneGq/M7ThX2+FUZlcHxeobjDF9smAxDMLRGBf/dgsH2yIsnV3MCTMLAXjsjWr+vHYvAB+oKOfDy2alNB0Bt29DRww6GzHVBCHfD23RnjkLgBK349yBkNAeVWo7YHGh9bw2xhzKgsUgxWJKKBoj/gF85e5mDrZFOGpqPq/uquPVXXUARGIx6lqdZki3XHYsfm+qcxbOjb6z+WxHFH6w1UvAoyhCjq9nIAh4Id/nBImdrc7AhHOy+6khN8ZMaFY6PQgdkSinfP8pTr9zIy1xXaVf2NlAplf4j+ULu5ZduXQmD3z61K7PqQ4U4PSkBmhyq0m2uiPBBmNChkeZnnVorqE0E1bXe1ixy4sHZaa1aDLG9MFyFoNQ1RBkf5NTG/zqnibOPcJptfR2QwezizI5dV4Jly6exruPm8ap80r6nUM7VWZmg1+UNfVCSYaypVkIeJQbj471O2RHvk8BYUGuckpJzCq3jTF9smAxCNWN3UO6vrSrsStY7GkIMqcwk0yfl1uvOKHHPnd85EQKs0dmflG/B47KV15v9PBGo5Lvh1nZicd2AqfX9lH5qU+jMWZssmAxCNVNzpwT2X4PexqcHEZMlT0NHZw1u+9xMZYfM2XE0gdwebkyOTPGkzUeGsJwUtHAdRDvmqqUZMZYkOSwHsaYicmCxSBUNzoBYmFpgMZghJgq66paCEWVGQWjY0CkLC/Myemum+irniJeUQZcOMVaQBljBmYl1EloD0V5adsB1u2pJy/gY2peBg3tEe5ZXc2Hfv8mAHOLRkewgJ5NZPuai8IYYwYrZcFCRFaISI2IbOhnvYjIz0Vkm4isF5ElceuuEpG33NdVqUpjIk9v3k91Y5BbHtvEh3+1kic27mdOaQ6FAS8NwQiv7W3p2nZB6ejpzZYTV6+eb8HCGDMMUlkMdQ9wG/CbftZfCMx3X8uA/wGWiUgxcANQgTPl2xoReVhV61OY1h6e2VLDc1tquedfuwAI+D2869ipfPy02cwqyWHFk2/QFIySE9faqSAwekr0suOSkjt6kmWMGcNSditR1edFZPYAm1wK/EZVFXhFRApFZCpwNvCkqtYBiMiTwHLgvlSks6Ujws2PbAJg475GirIzWLe7gdZQ95hOuZk+/vPCI5nhjgRbGPCiwM46p8L7vPnJDfw3UuKH+vDYJETGmGGQzufO6cCeuM+V7rL+lh9CRK4BrgGYOXPmkBIRjsR4bmstLR2RHoP+PXzdaSycksfTb9awdE5x1/DhAAVZzp9t64F2zp5XyC/eM59YqGNI5zfGmLEgnRXcfT3z6gDLD12oeqeqVqhqRVlZ2ZASUZSTwStffwcvX38uHznZCTgnzy3muPJCMn1eLjx2ao9AAU7OAqA9HGNy7uitFMj2WisnY8zwSGfOohKYEfe5HKhyl5/da/mzqU5MXsDPzZceQ0GWnwuPGXi+idmFmfg9QjimLJo8OsfHuPnoaJ9R1xhjhiKdweJh4DoRuR+ngrtRVfeJyBPA90SksyLgfOD6kUiQiPDVC45MuN3Mwkxe/cISIjElL3N01iBnjexII8aYcS5ldzoRuQ8nh1AqIpU4LZz8AKp6B/AYcBGwDWgDPu6uqxORm4FV7qFu6qzsHk2y/HY3NsZMHKlsDXVlgvUKfK6fdSuAFalIlzHGmMGzHtzGGGMSsmBhjDEmIQsWxhhjErJgYYwxJiELFsYYYxKyYGGMMSYhcVqwjn0i0gxs6bW4AGgcplN0HWtyaekkn8/bo9lxJBKN7D9woCaJ48wEdg/25D5PxiQR6XHOmEYDHvEG+9snWcN1HIBILFzg8/iT+puraiQSCw30NxuO/7/h/A4M6f+uHyn5bgJkZ+RM8ohn0M3ig+FgQcAfSCpNMY1F2kKtNQBlBYWTvJ6ev4dgKBQIZGQMy3dqOI/V2NpSUJCT2xiNRSO1jQ3J/F4Hclj/h4XZ+ZO8Ho8vFAkHMnz+w7q+aCwWaWhrqmFo39GFqpp4rkxVHRcvYHUfy+4cxuMPy7GA2lGYpuH8O42q6xvP15aC78G4vr7Reo3pvr6+7p19vcZ7MdQjo/BYDcN0HBi+NA3n32m0Xd94vrbhPtZ4vz4Yndc4Wq+vh/FUDLVaVSvSnY5Exko6h2o8X994vjYY/9cH4/8ah3J9yZCQqQAAAAW+SURBVO4znnIWd6Y7AUkaK+kcqvF8feP52mD8Xx+M/2scyvUltc+4yVkYY4xJnfGUszDGGJMiFiyMMcYkZMEiBUSkXEQeEpG3RGS7iNwqIhkDbP9FEckeyTQeLhFpSXcaUkVELhMRFZHEM2GNYYn+D0XkWREZU5XBE+G3B+n5/VmwGGYiIsBfgAdVdT6wAMgFbhlgty8CY+4LO45dCbwIXDGYnUTEZsRKI/vtpZYFi+F3LhBU1bsBVDUKfAn4hIjkiMiPROQNEVkvIp8XkS8A04BnROSZNKZ70EQkV0SeEpG17jVd6i6fLSJvishdIrJRRP4hIlnpTm8yRCQXOA34JG6wEJGzReR5EfmriGwSkTtExOOuaxGRm0RkJXBK+lI+NO61/S3u820i8rE0JulwTJjfHoz878+CxfA7GlgTv0BVm3C64F8NzAFOUNXjgHtV9edAFXCOqp4z0ok9TEHgMlVdApwD/Nh9ugOYD9yuqkfjdBR6X5rSOFjvAf6uqluBOhFZ4i5fCvw/4FhgHvBed3kOsEFVl6nqiyOeWhNvIv32YIR/fxYshp8AfbVHFuBM4A5VjYAz3/hIJiwFBPieiKwH/glMBya763aq6jr3/Rpg9sgnb0iuBO5339/vfgZ4VVV3uE+r9wGnu8ujwJ//f3t39yJ1Fcdx/P3BHmwjRSPLYtO6KSSIKCyii2K7CSINkrIiu+jCuwyEQrrpzgvrD+gBNJBgw6IuDJTooguJZCtTjKIHExE3MNSglrU+XZyzsW06D8404+x8XrAw85vfnD3D+X35znn4neltFeM8hin2oMfx97/9BvcQO8ScLC5pETAK/MC5L+ZB9RRwDXCn7WlJPwEL62tTs877E7joh6EkXU0ZyrhNkoEFlPbazX/bbeb5HzWBDKqz/PtL48LznTgAhin2oMfxl55F930MjEh6Bv6Z9HwV2A7sATbO7B4raWl9zxmg+a6PF5/FwGS9UB8AVvS7Qh16DHjb9grbK22PAj9SehGrJd1U5yoep0yAzwdHgFWSLpe0GBjrd4U6MEyxBz2OvySLLnO5Jf5RYJ2k74BvKWOLW4A3KeOnByR9BTxZ3/Y68NGgTLLVgJsCdgJ3SdpP+ZbzTV8r1rn1wPtzju2itNM+YCtwkJJA5p43UGba0PZRYBw4QGnPL/pasQ4MQ+xB/+Iv231E2yTdDrxhe3W/69ILku4HNtt+uN916ZZha8P5pF9tl55FtEXSRsoE78v9rktcmLTh4Opn26VnERERTaVnEQ1JGpX0Sb3J55Ck5+vxpZL21m0V9kpaUo/fKmmfpClJm2eVc4ukL2f9nZa0qV+fK2IQdCv+6msv1DIOSnpHUlsr39KziIYkLQeW256QdBVlzfZa4FngpO2tkl4Clth+UdIyyqqMtcCvtredo8wFwDHgbttHevVZIgZNt+JP0g2UFXyrbP8uaRzYbXt7q3VJzyIasn3c9kR9fAY4TLn5Zw2wo562g3JxYnvS9ufAdINix4DvkygiGuty/F0CXFFXU41Q7l5vWZJFtEzSSuAO4DPgWtvHoVzQwLI2inqCMkkXES3qJP5sHwO2UZYPHwdO2d7Tzv9PsoiWqGywtwvYVPfbudByLgMeAd7tVt0i5rtO46/Oaayh7I91PXClpKfbKSPJIpqSdCnlQt1p+716+EQdT50ZV51ssbiHgAnbJ7pf04j5p0vx9yBlv6hfbE9TtnK/t516JFlEQ3UXy7eAw7Zfm/XSh8CG+ngD8EGLRa4nQ1ARLeli/P0M3CNppJY5Rpn/aL0uWQ0VjUi6D/gU+Br4qx7eQhk3HQdupFyI62yflHQdsB9YVM//jbIC47TKL5IdBW62faq3nyRi8HQ5/l6h7Gt2lrKty3O2Z2842LguSRYREdFMhqEiIqKpJIuIiGgqySIiIppKsoiIiKaSLCIioqkki4iIaCrJIiIimkqyiIiIpv4GO8qCRpxrYOgAAAAASUVORK5CYII=\n", - "text/plain": [ - "" - ] - }, - "metadata": {}, - "output_type": "display_data" - } - ], + "outputs": [], "source": [ "pf.plot_drawdown_periods(df, 5)" ] }, { "cell_type": "code", - "execution_count": 8, + "execution_count": null, "metadata": {}, - "outputs": [ - { - "data": { - "text/plain": [ - "" - ] - }, - "execution_count": 8, - "metadata": {}, - "output_type": "execute_result" - }, - { - "data": { - "image/png": "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\n", - "text/plain": [ - "" - ] - }, - "metadata": {}, - "output_type": "display_data" - } - ], + "outputs": [], "source": [ "pf.plot_drawdown_underwater(df)" ] }, { "cell_type": "code", - "execution_count": 9, + "execution_count": null, "metadata": {}, - "outputs": [ - { - "data": { - "text/plain": [ - "" - ] - }, - "execution_count": 9, - "metadata": {}, - "output_type": "execute_result" - }, - { - "data": { - "image/png": "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\n", - "text/plain": [ - "" - ] - }, - "metadata": {}, - "output_type": "display_data" - } - ], + "outputs": [], "source": [ "pf.plot_returns(df)" ] }, { "cell_type": "code", - "execution_count": 10, + "execution_count": null, "metadata": {}, - "outputs": [ - { - "data": { - "text/plain": [ - "" - ] - }, - "execution_count": 10, - "metadata": {}, - "output_type": "execute_result" - }, - { - "data": { - "image/png": "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\n", - "text/plain": [ - "" - ] - }, - "metadata": {}, - "output_type": "display_data" - } - ], + "outputs": [], "source": [ "pf.plot_rolling_returns(df)" ] }, { "cell_type": "code", - "execution_count": 11, + "execution_count": null, "metadata": {}, - "outputs": [ - { - "data": { - "text/plain": [ - "" - ] - }, - "execution_count": 11, - "metadata": {}, - "output_type": "execute_result" - }, - { - "data": { - "image/png": "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\n", - "text/plain": [ - "" - ] - }, - "metadata": {}, - "output_type": "display_data" - } - ], + "outputs": [], "source": [ "pf.plot_annual_returns(df)" ] }, { "cell_type": "code", - "execution_count": 12, + "execution_count": null, "metadata": {}, - "outputs": [ - { - "data": { - "text/plain": [ - "" - ] - }, - "execution_count": 12, - "metadata": {}, - "output_type": "execute_result" - }, - { - "data": { - "image/png": "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\n", - "text/plain": [ - "" - ] - }, - "metadata": {}, - "output_type": "display_data" - } - ], + "outputs": [], "source": [ "pf.plot_monthly_returns_dist(df)" ] }, { "cell_type": "code", - "execution_count": 13, + "execution_count": null, "metadata": {}, - "outputs": [ - { - "name": "stderr", - "output_type": "stream", - "text": [ - "/Users/markconway/anaconda/lib/python3.6/site-packages/numpy/core/fromnumeric.py:57: FutureWarning: 'argmin' is deprecated. Use 'idxmin' instead. The behavior of 'argmin' will be corrected to return the positional minimum in the future. Use 'series.values.argmin' to get the position of the minimum now.\n", - " return getattr(obj, method)(*args, **kwds)\n" - ] - }, - { - "data": { - "text/html": [ - "
\n", - "\n", - "\n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - "
Worst drawdown periodsNet drawdown in %Peak dateValley dateRecovery dateDuration
011.612016-10-242016-11-142017-01-1964
17.422017-06-082017-07-032017-07-1829
25.042017-11-282017-12-04NaTNaN
34.642017-07-242017-09-252017-10-0554
43.202016-09-072016-09-092016-09-157
\n", - "
" - ], - "text/plain": [ - "Worst drawdown periods Net drawdown in % Peak date Valley date Recovery date \\\n", - "0 11.61 2016-10-24 2016-11-14 2017-01-19 \n", - "1 7.42 2017-06-08 2017-07-03 2017-07-18 \n", - "2 5.04 2017-11-28 2017-12-04 NaT \n", - "3 4.64 2017-07-24 2017-09-25 2017-10-05 \n", - "4 3.20 2016-09-07 2016-09-09 2016-09-15 \n", - "\n", - "Worst drawdown periods Duration \n", - "0 64 \n", - "1 29 \n", - "2 NaN \n", - "3 54 \n", - "4 7 " - ] - }, - "metadata": {}, - "output_type": "display_data" - } - ], + "outputs": [], "source": [ "pf.show_worst_drawdown_periods(df)" ] }, { "cell_type": "code", - "execution_count": 14, + "execution_count": null, "metadata": {}, - "outputs": [ - { - "data": { - "text/plain": [ - "" - ] - }, - "execution_count": 14, - "metadata": {}, - "output_type": "execute_result" - }, - { - "data": { - "image/png": "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\n", - "text/plain": [ - "" - ] - }, - "metadata": {}, - "output_type": "display_data" - } - ], + "outputs": [], "source": [ "pf.plot_return_quantiles(df)" ] }, { "cell_type": "code", - "execution_count": 15, + "execution_count": null, "metadata": {}, - "outputs": [ - { - "data": { - "text/plain": [ - "" - ] - }, - "execution_count": 15, - "metadata": {}, - "output_type": "execute_result" - }, - { - "data": { - "image/png": "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\n", - "text/plain": [ - "" - ] - }, - "metadata": {}, - "output_type": "display_data" - } - ], + "outputs": [], "source": [ "pf.plot_rolling_sharpe(df)" ] @@ -481,7 +155,7 @@ "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", - "version": "3.6.4" + "version": "3.6.6" } }, "nbformat": 4, From 482376758e7acd0fc77e778fc3459eb30ec2ec59 Mon Sep 17 00:00:00 2001 From: Mark Conway Date: Sun, 16 Sep 2018 10:27:28 -0400 Subject: [PATCH 049/129] update to version 2.3.3 and update dependencies update to version 2.3.3 and update dependencies --- docs/conf.py | 4 ++-- environment.yml | 20 ++++++++++---------- setup.py | 10 +++++----- 3 files changed, 17 insertions(+), 17 deletions(-) diff --git a/docs/conf.py b/docs/conf.py index 45fe40f..f3bced0 100644 --- a/docs/conf.py +++ b/docs/conf.py @@ -62,9 +62,9 @@ # built documents. # # The short X.Y version. -version = '2.3.2' +version = '2.3.3' # The full version, including alpha/beta/rc tags. -release = '2.3.2' +release = '2.3.3' # The language for content autogenerated by Sphinx. Refer to documentation # for a list of supported languages. diff --git a/environment.yml b/environment.yml index 013651c..8ce3e8a 100644 --- a/environment.yml +++ b/environment.yml @@ -5,17 +5,17 @@ channels: dependencies: - bokeh>=0.12 -- ipython>=3.2.3 +- ipython>=5.0 - matplotlib>=2.0.0 -- numpy>=1.9.1 -- pandas>=0.22.0 +- numpy>=1.12 +- pandas>=0.22 - pyyaml>=3.12 -- scikit-learn>=0.17.1 -- scipy>=0.18.1 -- seaborn>=0.7.1 -- xgboost>=0.6a2 +- scikit-learn>=0.19 +- scipy>=1.0 +- seaborn>=0.8 +- xgboost>=0.71 - pip: - category_encoders>=1.2.0 - - imbalanced-learn>=0.2.1 - - pandas-datareader>=0.3 - - pyfolio>=0.7 \ No newline at end of file + - imbalanced-learn>=0.3 + - pandas-datareader>=0.6 + - pyfolio>=0.8 \ No newline at end of file diff --git a/setup.py b/setup.py index e77b108..c53aea7 100644 --- a/setup.py +++ b/setup.py @@ -10,7 +10,7 @@ MAINTAINER_EMAIL = 'scottfree.analytics@scottfreellc.com' URL = "https://github.com/ScottFreeLLC/AlphaPy" LICENSE = "Apache License, Version 2" -VERSION = "2.3.2" +VERSION = "2.3.3" classifiers = ['Development Status :: 4 - Beta', 'Programming Language :: Python', @@ -26,17 +26,17 @@ install_reqs = [ 'bokeh>=0.12', 'category_encoders>=1.2.0', - 'imbalanced-learn>=0.2.1', - 'ipython>=3.2.3', + 'imbalanced-learn>=0.3', + 'ipython>=5.0', 'matplotlib>=2.0.0', 'numpy>=1.12', 'pandas>=0.22', 'pandas-datareader>=0.6', 'pyfolio>=0.8', 'pyyaml>=3.12', - 'scikit-learn>=0.18', + 'scikit-learn>=0.19', 'scipy>=1.0', - 'seaborn>=0.7.1', + 'seaborn>=0.8', 'xgboost>=0.71', ] From 4ed2d01bad51bc8c152198f4b22cd92324dfd5c1 Mon Sep 17 00:00:00 2001 From: Mark Conway Date: Fri, 4 Jan 2019 15:51:24 -0500 Subject: [PATCH 050/129] Add Keras to dependencies Add Keras to dependencies --- docs/conf.py | 6 +++--- environment.yml | 3 ++- setup.py | 3 ++- 3 files changed, 7 insertions(+), 5 deletions(-) diff --git a/docs/conf.py b/docs/conf.py index f3bced0..b27badb 100644 --- a/docs/conf.py +++ b/docs/conf.py @@ -54,7 +54,7 @@ # General information about the project. project = 'AlphaPy' -copyright = '2018, ScottFree Analytics LLC' +copyright = '2019, ScottFree Analytics LLC' author = 'Robert D. Scott II, Mark Conway' # The version info for the project you're documenting, acts as replacement for @@ -62,9 +62,9 @@ # built documents. # # The short X.Y version. -version = '2.3.3' +version = '2.3.4' # The full version, including alpha/beta/rc tags. -release = '2.3.3' +release = '2.3.4' # The language for content autogenerated by Sphinx. Refer to documentation # for a list of supported languages. diff --git a/environment.yml b/environment.yml index 8ce3e8a..782016f 100644 --- a/environment.yml +++ b/environment.yml @@ -6,6 +6,7 @@ channels: dependencies: - bokeh>=0.12 - ipython>=5.0 +- keras>=2.2 - matplotlib>=2.0.0 - numpy>=1.12 - pandas>=0.22 @@ -18,4 +19,4 @@ dependencies: - category_encoders>=1.2.0 - imbalanced-learn>=0.3 - pandas-datareader>=0.6 - - pyfolio>=0.8 \ No newline at end of file + - pyfolio>=0.8 diff --git a/setup.py b/setup.py index c53aea7..9018252 100644 --- a/setup.py +++ b/setup.py @@ -10,7 +10,7 @@ MAINTAINER_EMAIL = 'scottfree.analytics@scottfreellc.com' URL = "https://github.com/ScottFreeLLC/AlphaPy" LICENSE = "Apache License, Version 2" -VERSION = "2.3.3" +VERSION = "2.3.4" classifiers = ['Development Status :: 4 - Beta', 'Programming Language :: Python', @@ -28,6 +28,7 @@ 'category_encoders>=1.2.0', 'imbalanced-learn>=0.3', 'ipython>=5.0', + 'keras>=2.2', 'matplotlib>=2.0.0', 'numpy>=1.12', 'pandas>=0.22', From 79f80c4e7a5aa6ba34c105c70fdc46f1a0d48988 Mon Sep 17 00:00:00 2001 From: Mark Conway Date: Mon, 7 Jan 2019 15:41:50 -0500 Subject: [PATCH 051/129] Add TensorFlow to dependencies Add TensorFlow to dependencies --- docs/conf.py | 4 ++-- environment.yml | 1 + setup.py | 3 ++- 3 files changed, 5 insertions(+), 3 deletions(-) diff --git a/docs/conf.py b/docs/conf.py index b27badb..185e640 100644 --- a/docs/conf.py +++ b/docs/conf.py @@ -62,9 +62,9 @@ # built documents. # # The short X.Y version. -version = '2.3.4' +version = '2.3.5' # The full version, including alpha/beta/rc tags. -release = '2.3.4' +release = '2.3.5' # The language for content autogenerated by Sphinx. Refer to documentation # for a list of supported languages. diff --git a/environment.yml b/environment.yml index 782016f..5738ded 100644 --- a/environment.yml +++ b/environment.yml @@ -14,6 +14,7 @@ dependencies: - scikit-learn>=0.19 - scipy>=1.0 - seaborn>=0.8 +- tensorflow>=1.12 - xgboost>=0.71 - pip: - category_encoders>=1.2.0 diff --git a/setup.py b/setup.py index 9018252..d9c0f14 100644 --- a/setup.py +++ b/setup.py @@ -10,7 +10,7 @@ MAINTAINER_EMAIL = 'scottfree.analytics@scottfreellc.com' URL = "https://github.com/ScottFreeLLC/AlphaPy" LICENSE = "Apache License, Version 2" -VERSION = "2.3.4" +VERSION = "2.3.5" classifiers = ['Development Status :: 4 - Beta', 'Programming Language :: Python', @@ -38,6 +38,7 @@ 'scikit-learn>=0.19', 'scipy>=1.0', 'seaborn>=0.8', + 'tensorflow>=1.2' 'xgboost>=0.71', ] From ed137af65263710f78e08f3b4661e97c23071992 Mon Sep 17 00:00:00 2001 From: Mark Conway Date: Mon, 7 Jan 2019 16:16:29 -0500 Subject: [PATCH 052/129] update sklearn version and fix setup.py --- docs/conf.py | 4 ++-- environment.yml | 2 +- setup.py | 6 +++--- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/docs/conf.py b/docs/conf.py index 185e640..d107aea 100644 --- a/docs/conf.py +++ b/docs/conf.py @@ -62,9 +62,9 @@ # built documents. # # The short X.Y version. -version = '2.3.5' +version = '2.3.6' # The full version, including alpha/beta/rc tags. -release = '2.3.5' +release = '2.3.6' # The language for content autogenerated by Sphinx. Refer to documentation # for a list of supported languages. diff --git a/environment.yml b/environment.yml index 5738ded..8356502 100644 --- a/environment.yml +++ b/environment.yml @@ -11,7 +11,7 @@ dependencies: - numpy>=1.12 - pandas>=0.22 - pyyaml>=3.12 -- scikit-learn>=0.19 +- scikit-learn>=0.20 - scipy>=1.0 - seaborn>=0.8 - tensorflow>=1.12 diff --git a/setup.py b/setup.py index d9c0f14..05e6ebe 100644 --- a/setup.py +++ b/setup.py @@ -10,7 +10,7 @@ MAINTAINER_EMAIL = 'scottfree.analytics@scottfreellc.com' URL = "https://github.com/ScottFreeLLC/AlphaPy" LICENSE = "Apache License, Version 2" -VERSION = "2.3.5" +VERSION = "2.3.6" classifiers = ['Development Status :: 4 - Beta', 'Programming Language :: Python', @@ -35,10 +35,10 @@ 'pandas-datareader>=0.6', 'pyfolio>=0.8', 'pyyaml>=3.12', - 'scikit-learn>=0.19', + 'scikit-learn>=0.20', 'scipy>=1.0', 'seaborn>=0.8', - 'tensorflow>=1.2' + 'tensorflow>=1.2', 'xgboost>=0.71', ] From d1d4ff52546ea97ebcb6cbfc00df4d70be4a8c73 Mon Sep 17 00:00:00 2001 From: Mark Conway Date: Sun, 17 Mar 2019 10:14:36 -0400 Subject: [PATCH 053/129] update scorers update scorers --- alphapy/estimators.py | 56 +++++++++++++++++++++++-------------------- alphapy/model.py | 39 ++++++++++++++++++++++-------- alphapy/plots.py | 9 +++---- docs/conf.py | 4 ++-- setup.py | 27 ++++++++++----------- 5 files changed, 79 insertions(+), 56 deletions(-) diff --git a/alphapy/estimators.py b/alphapy/estimators.py index 51673c0..921b6eb 100644 --- a/alphapy/estimators.py +++ b/alphapy/estimators.py @@ -4,7 +4,7 @@ # Module : estimators # Created : July 11, 2013 # -# Copyright 2017 ScottFree Analytics LLC +# Copyright 2019 ScottFree Analytics LLC # Mark Conway & Robert D. Scott II # # Licensed under the Apache License, Version 2.0 (the "License"); @@ -70,33 +70,37 @@ # Define scorers # -scorers = {'accuracy' : (ModelType.classification, Objective.maximize), - 'average_precision' : (ModelType.classification, Objective.maximize), - 'f1' : (ModelType.classification, Objective.maximize), - 'f1_macro' : (ModelType.classification, Objective.maximize), - 'f1_micro' : (ModelType.classification, Objective.maximize), - 'f1_samples' : (ModelType.classification, Objective.maximize), - 'f1_weighted' : (ModelType.classification, Objective.maximize), - 'neg_log_loss' : (ModelType.classification, Objective.minimize), - 'precision' : (ModelType.classification, Objective.maximize), - 'recall' : (ModelType.classification, Objective.maximize), - 'roc_auc' : (ModelType.classification, Objective.maximize), - 'adjusted_rand_score' : (ModelType.clustering, Objective.maximize), - 'mean_absolute_error' : (ModelType.regression, Objective.minimize), - 'neg_mean_squared_error' : (ModelType.regression, Objective.minimize), - 'median_absolute_error' : (ModelType.regression, Objective.minimize), - 'r2' : (ModelType.regression, Objective.maximize)} +scorers = {'accuracy' : (ModelType.classification, Objective.maximize), + 'average_precision' : (ModelType.classification, Objective.maximize), + 'balanced_accuracy' : (ModelType.classification, Objective.maximize), + 'brier_score_loss' : (ModelType.classification, Objective.minimize), + 'f1' : (ModelType.classification, Objective.maximize), + 'f1_macro' : (ModelType.classification, Objective.maximize), + 'f1_micro' : (ModelType.classification, Objective.maximize), + 'f1_samples' : (ModelType.classification, Objective.maximize), + 'f1_weighted' : (ModelType.classification, Objective.maximize), + 'neg_log_loss' : (ModelType.classification, Objective.minimize), + 'precision' : (ModelType.classification, Objective.maximize), + 'recall' : (ModelType.classification, Objective.maximize), + 'roc_auc' : (ModelType.classification, Objective.maximize), + 'adjusted_rand_score' : (ModelType.clustering, Objective.maximize), + 'explained_variance' : (ModelType.regression, Objective.maximize), + 'neg_mean_absolute_error' : (ModelType.regression, Objective.minimize), + 'neg_mean_squared_error' : (ModelType.regression, Objective.minimize), + 'neg_mean_squared_log_error' : (ModelType.regression, Objective.minimize), + 'neg_median_absolute_error' : (ModelType.regression, Objective.minimize), + 'r2' : (ModelType.regression, Objective.maximize)} # # Define XGB scoring map # -xgb_score_map = {'neg_log_loss' : 'logloss', - 'mean_absolute_error' : 'mae', - 'neg_mean_squared_error' : 'rmse', - 'precision' : 'map', - 'roc_auc' : 'auc'} +xgb_score_map = {'neg_log_loss' : 'logloss', + 'neg_mean_absolute_error' : 'mae', + 'neg_mean_squared_error' : 'rmse', + 'precision' : 'map', + 'roc_auc' : 'auc'} # @@ -120,16 +124,16 @@ class Estimator: """ # __new__ - + def __new__(cls, algorithm, model_type, estimator, grid): return super(Estimator, cls).__new__(cls) - + # __init__ - + def __init__(self, algorithm, model_type, @@ -139,7 +143,7 @@ def __init__(self, self.model_type = model_type self.estimator = estimator self.grid = grid - + # __str__ def __str__(self): diff --git a/alphapy/model.py b/alphapy/model.py index 822ad0b..851e24a 100644 --- a/alphapy/model.py +++ b/alphapy/model.py @@ -4,7 +4,7 @@ # Module : model # Created : July 11, 2013 # -# Copyright 2017 ScottFree Analytics LLC +# Copyright 2019 ScottFree Analytics LLC # Mark Conway & Robert D. Scott II # # Licensed under the Apache License, Version 2.0 (the "License"); @@ -54,6 +54,7 @@ from sklearn.metrics import accuracy_score from sklearn.metrics import auc from sklearn.metrics import average_precision_score +from sklearn.metrics import balanced_accuracy_score from sklearn.metrics import brier_score_loss from sklearn.metrics import classification_report from sklearn.metrics import cohen_kappa_score @@ -63,6 +64,7 @@ from sklearn.metrics import log_loss from sklearn.metrics import mean_absolute_error from sklearn.metrics import mean_squared_error +from sklearn.metrics import mean_squared_log_error from sklearn.metrics import median_absolute_error from sklearn.metrics import precision_score from sklearn.metrics import r2_score @@ -70,6 +72,7 @@ from sklearn.metrics import roc_auc_score from sklearn.metrics import roc_curve from sklearn.metrics.cluster import adjusted_rand_score +from sklearn.model_selection import cross_val_score from sklearn.model_selection import train_test_split import sys import yaml @@ -134,9 +137,9 @@ class Model: stored in ``algolist``. """ - + # __init__ - + def __init__(self, specs): # specifications @@ -170,7 +173,7 @@ def __init__(self, self.probas = {} # Keys: (algorithm, partition, metric) self.metrics = {} - + # __str__ def __str__(self): @@ -649,11 +652,14 @@ def first_fit(model, algo, est): # Extract model parameters. + cv_folds = model.specs['cv_folds'] esr = model.specs['esr'] model_type = model.specs['model_type'] + n_jobs = model.specs['n_jobs'] scorer = model.specs['scorer'] seed = model.specs['seed'] split = model.specs['split'] + verbosity = model.specs['verbosity'] # Extract model data. @@ -662,7 +668,6 @@ def first_fit(model, algo, est): # Fit the initial model. - algo_keras = 'KERAS' in algo algo_xgb = 'XGB' in algo if algo_xgb and scorer in xgb_score_map: @@ -675,8 +680,14 @@ def first_fit(model, algo, est): else: est.fit(X_train, y_train) - # Store the estimator + # Get the initial scores + + logger.info("Cross-Validation") + scores = cross_val_score(est, X_train, y_train, scoring=scorer, cv=cv_folds, + n_jobs=n_jobs, verbose=verbosity) + logger.info("Cross-Validation Scores: %s", scores) + # Store the estimator model.estimators[algo] = est # Record importances and coefficients if necessary. @@ -965,7 +976,7 @@ def predict_blend(model): model.probas[(blend_tag, Partition.test)] = clf.predict_proba(X_blend_test)[:, 1] else: alphas = [0.0001, 0.005, 0.001, 0.005, 0.01, 0.05, 0.1, 0.5, - 1.0, 5.0, 10.0, 50.0, 100.0, 500.0, 1000.0] + 1.0, 5.0, 10.0, 50.0, 100.0, 500.0, 1000.0] rcvr = RidgeCV(alphas=alphas, normalize=True, cv=cv_folds) rcvr.fit(X_blend_train, y_train) model.estimators[blend_tag] = rcvr @@ -1056,7 +1067,11 @@ def generate_metrics(model, partition): except: logger.info("Average Precision Score not calculated") try: - model.metrics[(algo, partition, 'brier_score')] = brier_score_loss(expected, probas) + model.metrics[(algo, partition, 'balanced_accuracy')] = balanced_accuracy_score(expected, predicted) + except: + logger.info("Accuracy Score not calculated") + try: + model.metrics[(algo, partition, 'brier_score_loss')] = brier_score_loss(expected, probas) except: logger.info("Brier Score not calculated") try: @@ -1095,17 +1110,21 @@ def generate_metrics(model, partition): except: logger.info("Explained Variance Score not calculated") try: - model.metrics[(algo, partition, 'mean_absolute_error')] = mean_absolute_error(expected, predicted) + model.metrics[(algo, partition, 'neg_mean_absolute_error')] = mean_absolute_error(expected, predicted) except: logger.info("Mean Absolute Error not calculated") try: - model.metrics[(algo, partition, 'median_absolute_error')] = median_absolute_error(expected, predicted) + model.metrics[(algo, partition, 'neg_median_absolute_error')] = median_absolute_error(expected, predicted) except: logger.info("Median Absolute Error not calculated") try: model.metrics[(algo, partition, 'neg_mean_squared_error')] = mean_squared_error(expected, predicted) except: logger.info("Mean Squared Error not calculated") + try: + model.metrics[(algo, partition, 'neg_mean_squared_log_error')] = mean_squared_log_error(expected, predicted) + except: + logger.info("Mean Squared Log Error not calculated") try: model.metrics[(algo, partition, 'r2')] = r2_score(expected, predicted) except: diff --git a/alphapy/plots.py b/alphapy/plots.py index ada107d..ee1a44b 100644 --- a/alphapy/plots.py +++ b/alphapy/plots.py @@ -4,7 +4,7 @@ # Module : plots # Created : July 11, 2013 # -# Copyright 2017 ScottFree Analytics LLC +# Copyright 2019 ScottFree Analytics LLC # Mark Conway & Robert D. Scott II # # Licensed under the Apache License, Version 2.0 (the "License"); @@ -67,8 +67,9 @@ from itertools import product import logging import math +import matplotlib +matplotlib.use('PS') import matplotlib.pyplot as plt -plt.switch_backend('agg') from mpl_toolkits.mplot3d import Axes3D import numpy as np import pandas as pd @@ -469,7 +470,7 @@ def plot_learning_curve(model, partition): cv = StratifiedKFold(n_splits=cv_folds, shuffle=shuffle, random_state=seed) - # Plot a learning curve for each algorithm. + # Plot a learning curve for each algorithm. ylim = (0.4, 1.01) @@ -715,7 +716,7 @@ def plot_validation_curve(model, partition, pname, prange): alpha = 0.2 # Calculate a validation curve for each algorithm. - + for algo in model.algolist: logger.info("Algorithm: %s", algo) # get estimator diff --git a/docs/conf.py b/docs/conf.py index d107aea..4f711dd 100644 --- a/docs/conf.py +++ b/docs/conf.py @@ -62,9 +62,9 @@ # built documents. # # The short X.Y version. -version = '2.3.6' +version = '2.3.7' # The full version, including alpha/beta/rc tags. -release = '2.3.6' +release = '2.3.7' # The language for content autogenerated by Sphinx. Refer to documentation # for a list of supported languages. diff --git a/setup.py b/setup.py index 05e6ebe..551fac6 100644 --- a/setup.py +++ b/setup.py @@ -10,7 +10,7 @@ MAINTAINER_EMAIL = 'scottfree.analytics@scottfreellc.com' URL = "https://github.com/ScottFreeLLC/AlphaPy" LICENSE = "Apache License, Version 2" -VERSION = "2.3.6" +VERSION = "2.3.7" classifiers = ['Development Status :: 4 - Beta', 'Programming Language :: Python', @@ -24,22 +24,21 @@ 'Operating System :: OS Independent'] install_reqs = [ - 'bokeh>=0.12', - 'category_encoders>=1.2.0', - 'imbalanced-learn>=0.3', - 'ipython>=5.0', + 'bokeh>=1.0', + 'category_encoders>=1.3', + 'imbalanced-learn>=0.4.3', + 'ipython>=7.2', 'keras>=2.2', - 'matplotlib>=2.0.0', - 'numpy>=1.12', - 'pandas>=0.22', - 'pandas-datareader>=0.6', - 'pyfolio>=0.8', + 'matplotlib>=3.0', + 'numpy>=1.15', + 'pandas>=0.24', + 'pandas-datareader>=0.7', + 'pyfolio>=0.9', 'pyyaml>=3.12', 'scikit-learn>=0.20', - 'scipy>=1.0', - 'seaborn>=0.8', - 'tensorflow>=1.2', - 'xgboost>=0.71', + 'scipy>=1.1', + 'seaborn>=0.9', + 'tensorflow>=1.13', ] if __name__ == "__main__": From eeb72c7d7f191b13c533374610e6bc3ad306c820 Mon Sep 17 00:00:00 2001 From: Mark Conway Date: Wed, 12 Jun 2019 12:25:59 -0400 Subject: [PATCH 054/129] Cross-Validation is sometimes failing because of joblib bug We use cross-validation to generate a set of initial scores for the model. Sometimes, this cross-validation fails because of a joblib error when running scikit-learning in parallel. One workaround is to set number_jobs = 1 in the model.yml file, but we want to catch this exception so the model because the initial CV step is not absolutely necessary. --- alphapy/model.py | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/alphapy/model.py b/alphapy/model.py index 851e24a..d0d6401 100644 --- a/alphapy/model.py +++ b/alphapy/model.py @@ -683,9 +683,12 @@ def first_fit(model, algo, est): # Get the initial scores logger.info("Cross-Validation") - scores = cross_val_score(est, X_train, y_train, scoring=scorer, cv=cv_folds, - n_jobs=n_jobs, verbose=verbosity) - logger.info("Cross-Validation Scores: %s", scores) + try: + scores = cross_val_score(est, X_train, y_train, scoring=scorer, cv=cv_folds, + n_jobs=n_jobs, verbose=verbosity) + logger.info("Cross-Validation Scores: %s", scores) + except: + logger.info("Cross-Validation Failed: Try setting number_jobs = 1 in model.yml") # Store the estimator model.estimators[algo] = est From 02f9b331dc47e0ba6ba1e98b542f60c5b9c82dc1 Mon Sep 17 00:00:00 2001 From: Mark Conway Date: Wed, 12 Jun 2019 12:35:39 -0400 Subject: [PATCH 055/129] version 2.3.8 version 2.3.8 --- docs/conf.py | 4 ++-- setup.py | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/docs/conf.py b/docs/conf.py index 4f711dd..3cc165c 100644 --- a/docs/conf.py +++ b/docs/conf.py @@ -62,9 +62,9 @@ # built documents. # # The short X.Y version. -version = '2.3.7' +version = '2.3.8' # The full version, including alpha/beta/rc tags. -release = '2.3.7' +release = '2.3.8' # The language for content autogenerated by Sphinx. Refer to documentation # for a list of supported languages. diff --git a/setup.py b/setup.py index 551fac6..f2c24b7 100644 --- a/setup.py +++ b/setup.py @@ -10,7 +10,7 @@ MAINTAINER_EMAIL = 'scottfree.analytics@scottfreellc.com' URL = "https://github.com/ScottFreeLLC/AlphaPy" LICENSE = "Apache License, Version 2" -VERSION = "2.3.7" +VERSION = "2.3.8" classifiers = ['Development Status :: 4 - Beta', 'Programming Language :: Python', From 7f60e5bfd0dc666f8b5ab8b95a3eb8795457118c Mon Sep 17 00:00:00 2001 From: Mark Conway Date: Wed, 7 Aug 2019 11:42:43 -0400 Subject: [PATCH 056/129] remove unused estimators remove unused estimators --- alphapy/estimators.py | 4 ---- 1 file changed, 4 deletions(-) diff --git a/alphapy/estimators.py b/alphapy/estimators.py index 921b6eb..2b8e29b 100644 --- a/alphapy/estimators.py +++ b/alphapy/estimators.py @@ -46,14 +46,10 @@ from sklearn.ensemble import RandomForestRegressor from sklearn.linear_model import LinearRegression from sklearn.linear_model import LogisticRegression -from sklearn.linear_model import RandomizedLasso -from sklearn.linear_model import RandomizedLogisticRegression -from sklearn.naive_bayes import GaussianNB from sklearn.naive_bayes import MultinomialNB from sklearn.neighbors import KNeighborsClassifier from sklearn.neighbors import KNeighborsRegressor from sklearn.svm import LinearSVC -from sklearn.svm import OneClassSVM from sklearn.svm import SVC import xgboost as xgb import yaml From 86622be1f4cd46bdffefad1d786192a2b95b98c6 Mon Sep 17 00:00:00 2001 From: Mark Conway Date: Wed, 7 Aug 2019 11:43:54 -0400 Subject: [PATCH 057/129] fix encoder bug fix encoder bug --- alphapy/features.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/alphapy/features.py b/alphapy/features.py index a70f9c7..f323daf 100644 --- a/alphapy/features.py +++ b/alphapy/features.py @@ -213,7 +213,7 @@ def zscore(vec): zscore = 0 return zscore - + # # Function runs_test # @@ -845,6 +845,7 @@ def get_factors(model, df, fnum, fname, nvalues, dtype, logger.info("Rounding: %d", rounding) feature = feature.apply(float_factor, args=[rounding]) # encoders + pd_features = pd.DataFrame() enc = None ef = pd.DataFrame(feature) if encoder == Encoders.factorize: @@ -1309,7 +1310,7 @@ def create_features(model, X): # standard processing of numerical, categorical, and text features if fc in factors: features = get_factors(model, X, fnum, fc, nunique, dtype, - encoder, rounding, sentinel) + encoder, rounding, sentinel) elif dtype == 'float64' or dtype == 'int64' or dtype == 'bool': features = get_numerical_features(fnum, fc, X, nunique, dtype, sentinel, logtransform, pvalue_level) From 9ed5f154d8b7fad0fcadc109bce74da2d6fb9740 Mon Sep 17 00:00:00 2001 From: Mark Conway Date: Tue, 13 Aug 2019 16:26:57 -0400 Subject: [PATCH 058/129] Update .gitignore Ignore .eggs/* --- .gitignore | 43 ++++++++++++++++--------------------------- 1 file changed, 16 insertions(+), 27 deletions(-) diff --git a/.gitignore b/.gitignore index ea6ac42..88bca4e 100644 --- a/.gitignore +++ b/.gitignore @@ -1,30 +1,19 @@ - -*.pyc - -*.iml - -.idea/.name - -*.egg-info* - -*build* - -*.whl - -*.gz - -.idea/encodings.xml - -.idea/inspectionProfiles/profiles_settings.xml - -.idea/misc.xml - -.idea/modules.xml - -.idea/vcs.xml - -.idea/workspace.xml -.idea/other.xml +*.pyc + +*.iml + +*.egg-info* + +*build* + +*.whl + +*.gz + +.idea/* + +.eggs/* + alphapy/examples/Trading System/.ipynb_checkpoints/A Trading System-checkpoint.ipynb *.pkl *.png From 3b7aea6478a512bb02b05928aff4c0248302bc04 Mon Sep 17 00:00:00 2001 From: Mark Conway Date: Fri, 20 Sep 2019 21:27:05 -0400 Subject: [PATCH 059/129] Update .travis.yml for Python 3.7 Remove Python 3.5 and add Python 3.7 -- current support is for versions 3.6 and 3.7. --- .travis.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.travis.yml b/.travis.yml index 6cc6a2a..e9e8231 100644 --- a/.travis.yml +++ b/.travis.yml @@ -2,8 +2,8 @@ language: python sudo: false python: - - "3.5" - "3.6" + - "3.7" before_install: # We do this conditionally because it saves us some downloading if the @@ -28,7 +28,7 @@ install: - pip install category_encoders - pip install imbalanced-learn - pip install pyfolio - + script: nosetests From 7a7d0e7b35cbee6b3409ac30ff4bf4edfe525af2 Mon Sep 17 00:00:00 2001 From: Mark Conway Date: Sun, 17 Nov 2019 15:58:09 -0500 Subject: [PATCH 060/129] Data Feed Update 2.3.9 Data Feed Update 2.3.9 --- .gitignore | 4 + alphapy/__main__.py | 23 +- alphapy/analysis.py | 15 +- alphapy/data.py | 412 ++++++++++++++---- alphapy/estimators.py | 2 +- alphapy/examples/NCAAB/config/model.yml | 7 +- .../examples/Trading Model/config/market.yml | 5 +- .../Trading System/A Trading System.ipynb | 24 +- .../examples/Trading System/config/market.yml | 5 +- alphapy/features.py | 19 +- alphapy/frame.py | 8 +- alphapy/globals.py | 9 +- alphapy/market_flow.py | 19 +- alphapy/model.py | 10 +- alphapy/plots.py | 70 +-- alphapy/sport_flow.py | 2 +- docs/conf.py | 4 +- environment.yml | 32 +- setup.py | 24 +- 19 files changed, 504 insertions(+), 190 deletions(-) diff --git a/.gitignore b/.gitignore index 88bca4e..7d37039 100644 --- a/.gitignore +++ b/.gitignore @@ -17,3 +17,7 @@ alphapy/examples/Trading System/.ipynb_checkpoints/A Trading System-checkpoint.ipynb *.pkl *.png +*.code-workspace +alphapy/.vscode/launch.json +alphapy/.vscode/settings.json +*.log diff --git a/alphapy/__main__.py b/alphapy/__main__.py index aaa6f50..3293902 100644 --- a/alphapy/__main__.py +++ b/alphapy/__main__.py @@ -66,6 +66,7 @@ import numpy as np import os import pandas as pd +from sklearn.model_selection import train_test_split import sys import warnings warnings.simplefilter(action='ignore', category=DeprecationWarning) @@ -115,11 +116,12 @@ def training_pipeline(model): feature_selection = model.specs['feature_selection'] grid_search = model.specs['grid_search'] model_type = model.specs['model_type'] - predict_mode = model.specs['predict_mode'] rfe = model.specs['rfe'] sampling = model.specs['sampling'] scorer = model.specs['scorer'] + seed = model.specs['seed'] separator = model.specs['separator'] + split = model.specs['split'] target = model.specs['target'] # Get train and test data @@ -127,6 +129,14 @@ def training_pipeline(model): X_train, y_train = get_data(model, Partition.train) X_test, y_test = get_data(model, Partition.test) + # If there is no test partition, then we will split the train partition + + if X_test.empty: + logger.info("No Test Data Found") + logger.info("Splitting Training Data") + X_train, X_test, y_train, y_test = train_test_split( + X_train, y_train, test_size=split, random_state=seed) + # Determine if there are any test labels if y_test.any(): @@ -311,11 +321,9 @@ def prediction_pipeline(model): directory = model.specs['directory'] drop = model.specs['drop'] - extension = model.specs['extension'] feature_selection = model.specs['feature_selection'] model_type = model.specs['model_type'] rfe = model.specs['rfe'] - separator = model.specs['separator'] # Get all data. We need original train and test for interactions. @@ -379,15 +387,12 @@ def prediction_pipeline(model): if model_type == ModelType.classification: model.probas[(tag, partition)] = predictor.predict_proba(all_features)[:, 1] - # Get date stamp to record file creation - - d = datetime.now() - f = "%Y%m%d" - timestamp = d.strftime(f) - # Save predictions save_predictions(model, tag, partition) + # Return the model + return model + # # Function main_pipeline diff --git a/alphapy/analysis.py b/alphapy/analysis.py index 367205d..111eb0f 100644 --- a/alphapy/analysis.py +++ b/alphapy/analysis.py @@ -4,7 +4,7 @@ # Module : analysis # Created : July 11, 2013 # -# Copyright 2017 ScottFree Analytics LLC +# Copyright 2019 ScottFree Analytics LLC # Mark Conway & Robert D. Scott II # # Licensed under the Apache License, Version 2.0 (the "License"); @@ -95,7 +95,7 @@ class Analysis(object): analyses = {} # __new__ - + def __new__(cls, model, group): @@ -123,7 +123,7 @@ def __init__(self, self.group = group # add analysis to analyses list Analysis.analyses[an] = self - + # __str__ def __str__(self): @@ -192,9 +192,6 @@ def run_analysis(analysis, lag_period, forecast_period, leaders, # Calculate split date logger.info("Analysis Dates") split_date = subtract_days(predict_date, predict_history) - logger.info("Train Date: %s", train_date) - logger.info("Split Date: %s", split_date) - logger.info("Test Date: %s", predict_date) # Load the data frames data_frames = load_frames(group, directory, extension, separator, splits) @@ -203,9 +200,11 @@ def run_analysis(analysis, lag_period, forecast_period, leaders, if predict_mode: # create predict frame + logger.info("Split Date for Prediction Mode: %s", split_date) predict_frame = pd.DataFrame() else: # create train and test frames + logger.info("Split Date for Training Mode: %s", predict_date) train_frame = pd.DataFrame() test_frame = pd.DataFrame() @@ -232,11 +231,11 @@ def run_analysis(analysis, lag_period, forecast_period, leaders, tag) else: # split data into train and test - new_train = df.loc[(df.index >= train_date) & (df.index < split_date)] + new_train = df.loc[(df.index >= train_date) & (df.index < predict_date)] if len(new_train) > 0: new_train = new_train.dropna() train_frame = train_frame.append(new_train) - new_test = df.loc[(df.index >= split_date) & (df.index <= last_date)] + new_test = df.loc[(df.index >= predict_date) & (df.index <= last_date)] if len(new_test) > 0: # check if target column has NaN values nan_count = df[target].isnull().sum() diff --git a/alphapy/data.py b/alphapy/data.py index bfcaa02..2421d2a 100644 --- a/alphapy/data.py +++ b/alphapy/data.py @@ -4,7 +4,7 @@ # Module : data # Created : July 11, 2013 # -# Copyright 2017 ScottFree Analytics LLC +# Copyright 2019 ScottFree Analytics LLC # Mark Conway & Robert D. Scott II # # Licensed under the Apache License, Version 2.0 (the "License"); @@ -31,14 +31,16 @@ from alphapy.frame import read_frame from alphapy.globals import ModelType from alphapy.globals import Partition, datasets -from alphapy.globals import PD_WEB_DATA_FEEDS from alphapy.globals import PSEP, SSEP, USEP from alphapy.globals import SamplingMethod from alphapy.globals import WILDCARD from alphapy.space import Space +import arrow from datetime import datetime from datetime import timedelta +from iexfinance.stocks import get_historical_data +from iexfinance.stocks import get_historical_intraday from imblearn.combine import SMOTEENN from imblearn.combine import SMOTETomek from imblearn.ensemble import BalanceCascade @@ -56,7 +58,9 @@ from imblearn.under_sampling import RepeatedEditedNearestNeighbours from imblearn.under_sampling import TomekLinks import logging +import math import numpy as np +import os import pandas as pd pd.core.common.is_list_like = pd.api.types.is_list_like import pandas_datareader.data as web @@ -64,6 +68,7 @@ import requests from scipy import sparse from sklearn.preprocessing import LabelEncoder +import sys # @@ -106,8 +111,11 @@ def get_data(model, partition): model_type = model.specs['model_type'] separator = model.specs['separator'] target = model.specs['target'] - test_file = model.test_file - train_file = model.train_file + + # Initialize X and y + + X = pd.DataFrame() + y = np.empty([0, 0]) # Read in the file @@ -115,34 +123,32 @@ def get_data(model, partition): input_dir = SSEP.join([directory, 'input']) df = read_frame(input_dir, filename, extension, separator) - # Assign target and drop it if necessary + # Get features and target - y = np.empty([0, 0]) - if target in df.columns: - logger.info("Found target %s in data frame", target) - # check if target column has NaN values - nan_count = df[target].isnull().sum() - if nan_count > 0: - logger.info("Found %d records with NaN target values", nan_count) - logger.info("Labels (y) for %s will not be used", partition) + if not df.empty: + if target in df.columns: + logger.info("Found target %s in data frame", target) + # check if target column has NaN values + nan_count = df[target].isnull().sum() + if nan_count > 0: + logger.info("Found %d records with NaN target values", nan_count) + logger.info("Labels (y) for %s will not be used", partition) + else: + # assign the target column to y + y = df[target] + # encode label only for classification + if model_type == ModelType.classification: + y = LabelEncoder().fit_transform(y) + logger.info("Labels (y) found for %s", partition) + # drop the target from the original frame + df = df.drop([target], axis=1) else: - # assign the target column to y - y = df[target] - # encode label only for classification - if model_type == ModelType.classification: - y = LabelEncoder().fit_transform(y) - logger.info("Labels (y) found for %s", partition) - # drop the target from the original frame - df = df.drop([target], axis=1) - else: - logger.info("Target %s not found in %s", target, partition) - - # Extract features - - if features == WILDCARD: - X = df - else: - X = df[features] + logger.info("Target %s not found in %s", target, partition) + # Extract features + if features == WILDCARD: + X = df + else: + X = df[features] # Labels are returned usually only for training data return X, y @@ -250,7 +256,7 @@ def sample_data(model): elif sampling_method == SamplingMethod.under_nearmiss: sampler = NearMiss(version=1) elif sampling_method == SamplingMethod.under_ncr: - sampler = NeighbourhoodCleaningRule(size_ngh=51) + sampler = NeighbourhoodCleaningRule() elif sampling_method == SamplingMethod.over_random: sampler = RandomOverSampler(ratio=ratio) elif sampling_method == SamplingMethod.over_smote: @@ -311,7 +317,7 @@ def convert_data(df, index_column, intraday_data): # Standardize column names df = df.rename(columns = lambda x: x.lower().replace(' ','')) - # Create the time/date index if not already done + # Create the time/date index if not already done if not isinstance(df.index, pd.DatetimeIndex): df.reset_index(inplace=True) @@ -376,10 +382,10 @@ def enhance_intraday_data(df): # -# Function get_google_data +# Function get_google_intraday_data # -def get_google_data(symbol, lookback_period, fractal): +def get_google_intraday_data(symbol, lookback_period, fractal): r"""Get Google Finance intraday data. We get intraday data from the Google Finance API, even though @@ -405,20 +411,22 @@ def get_google_data(symbol, lookback_period, fractal): # Google requires upper-case symbol, otherwise not found symbol = symbol.upper() - # convert fractal to interval + # Initialize data frame + df = pd.DataFrame() + # Convert fractal to interval interval = 60 * int(re.findall('\d+', fractal)[0]) # Google has a 50-day limit max_days = 50 if lookback_period > max_days: lookback_period = max_days - # set Google data constants + # Set Google data constants toffset = 7 line_length = 6 - # make the request to Google + # Make the request to Google base_url = 'https://finance.google.com/finance/getprices?q={}&i={}&p={}d&f=d,o,h,l,c,v' url = base_url.format(symbol, interval, lookback_period) response = requests.get(url) - # process the response + # Process the response text = response.text.split('\n') records = [] for line in text[toffset:]: @@ -441,79 +449,308 @@ def get_google_data(symbol, lookback_period, fractal): dt_time = dt.strftime('%H:%M:%S') record = (dt_date, dt_time, open_item, high_item, low_item, close_item, volume_item) records.append(record) - # create data frame + # Create data frame cols = ['date', 'time', 'open', 'high', 'low', 'close', 'volume'] df = pd.DataFrame.from_records(records, columns=cols) - # return the dataframe + # Return the dataframe return df # -# Function get_pandas_data +# Function get_google_data # -def get_pandas_data(schema, symbol, lookback_period): - r"""Get Pandas Web Reader data. +def get_google_data(schema, subschema, symbol, intraday_data, data_fractal, + from_date, to_date, lookback_period): + r"""Get data from Google. Parameters ---------- schema : str - The source of the pandas-datareader data. + The schema (including any subschema) for this data feed. + subschema : str + Any subschema for this data feed. symbol : str A valid stock symbol. + intraday_data : bool + If True, then get intraday data. + data_fractal : str + Pandas offset alias. + from_date : str + Starting date for symbol retrieval. + to_date : str + Ending date for symbol retrieval. lookback_period : int - The number of days of daily data to retrieve. + The number of periods of data to retrieve. Returns ------- df : pandas.DataFrame - The dataframe containing the intraday data. + The dataframe containing the market data. """ - # Quandl is a special case with subfeeds. + df = pd.DataFrame() + if intraday_data: + # use internal function + # df = get_google_intraday_data(symbol, lookback_period, data_fractal) + logger.info("Google Finance API for intraday data no longer available") + else: + # Google Finance API no longer available + logger.info("Google Finance API for daily data no longer available") + return df + + +# +# Function get_iex_data +# + +def get_iex_data(schema, subschema, symbol, intraday_data, data_fractal, + from_date, to_date, lookback_period): + r"""Get data from IEX. + + Parameters + ---------- + schema : str + The schema (including any subschema) for this data feed. + subschema : str + Any subschema for this data feed. + symbol : str + A valid stock symbol. + intraday_data : bool + If True, then get intraday data. + data_fractal : str + Pandas offset alias. + from_date : str + Starting date for symbol retrieval. + to_date : str + Ending date for symbol retrieval. + lookback_period : int + The number of periods of data to retrieve. + + Returns + ------- + df : pandas.DataFrame + The dataframe containing the market data. + + """ - if 'quandl' in schema: + symbol = symbol.upper() + df = pd.DataFrame() + + if intraday_data: + # use iexfinance function to get intraday data for each date + df = pd.DataFrame() + for d in pd.date_range(from_date, to_date): + dstr = d.strftime('%Y-%m-%d') + logger.info("%s Data for %s", symbol, dstr) + try: + df1 = get_historical_intraday(symbol, d, output_format="pandas") + df1_len = len(df1) + if df1_len > 0: + logger.info("%s: %d rows", symbol, df1_len) + df = df.append(df1) + else: + logger.info("%s: No Trading Data for %s", symbol, dstr) + except: + iex_error = "*** IEX Intraday Data Error (check Quota) ***" + logger.error(iex_error) + sys.exit(iex_error) + else: + # use iexfinance function for historical daily data try: - schema, symbol_prefix = schema.split(USEP) - symbol = SSEP.join([symbol_prefix, symbol]) + df = get_historical_data(symbol, from_date, to_date, output_format="pandas") except: - logger.info("Quandl schema format must be: quandl_DB. Ex: quandl_wiki") + iex_error = "*** IEX Daily Data Error (check Quota) ***" + logger.error(iex_error) + sys.exit(iex_error) + return df - # Calculate the start and end date. - start = datetime.now() - timedelta(lookback_period) - end = datetime.now() +# +# Function get_pandas_data +# + +def get_pandas_data(schema, subschema, symbol, intraday_data, data_fractal, + from_date, to_date, lookback_period): + r"""Get Pandas Web Reader data. + + Parameters + ---------- + schema : str + The schema (including any subschema) for this data feed. + subschema : str + Any subschema for this data feed. + symbol : str + A valid stock symbol. + intraday_data : bool + If True, then get intraday data. + data_fractal : str + Pandas offset alias. + from_date : str + Starting date for symbol retrieval. + to_date : str + Ending date for symbol retrieval. + lookback_period : int + The number of periods of data to retrieve. + + Returns + ------- + df : pandas.DataFrame + The dataframe containing the market data. + + """ # Call the Pandas Web data reader. - df = None try: - df = web.DataReader(symbol.upper(), schema, start, end) + df = web.DataReader(symbol, schema, from_date, to_date) except: - logger.info("Could not retrieve data for: %s", symbol) + df = pd.DataFrame() + logger.info("Could not retrieve %s data with pandas-datareader", symbol.upper()) return df +# +# Function get_quandl_data +# + +def get_quandl_data(schema, subschema, symbol, intraday_data, data_fractal, + from_date, to_date, lookback_period): + r"""Get Quandl data. + + Parameters + ---------- + schema : str + The schema for this data feed. + subschema : str + Any subschema for this data feed. + symbol : str + A valid stock symbol. + intraday_data : bool + If True, then get intraday data. + data_fractal : str + Pandas offset alias. + from_date : str + Starting date for symbol retrieval. + to_date : str + Ending date for symbol retrieval. + lookback_period : int + The number of periods of data to retrieve. + + Returns + ------- + df : pandas.DataFrame + The dataframe containing the market data. + + """ + + # Quandl is a special case with subfeeds. + + symbol = SSEP.join([subschema.upper(), symbol.upper()]) + + # Call the Pandas Web data reader. + + df = get_pandas_data(schema, subschema, symbol, intraday_data, data_fractal, + from_date, to_date, lookback_period) + + return df + + +# +# Function get_yahoo_data +# + +def get_yahoo_data(schema, subschema, symbol, intraday_data, data_fractal, + from_date, to_date, lookback_period): + r"""Get Yahoo data. + + Parameters + ---------- + schema : str + The schema (including any subschema) for this data feed. + subschema : str + Any subschema for this data feed. + symbol : str + A valid stock symbol. + intraday_data : bool + If True, then get intraday data. + data_fractal : str + Pandas offset alias. + from_date : str + Starting date for symbol retrieval. + to_date : str + Ending date for symbol retrieval. + lookback_period : int + The number of periods of data to retrieve. + + Returns + ------- + df : pandas.DataFrame + The dataframe containing the market data. + + """ + + df = pd.DataFrame() + if intraday_data: + url = 'https://query1.finance.yahoo.com/v8/finance/chart/' + data_range = ''.join([str(lookback_period), 'd']) + interval = int(''.join(filter(str.isdigit, data_fractal))) + fractal = re.sub(r'\d+', '', data_fractal) + mapper = {'H': 60, 'T': 1, 'min':1, 'S': 1./60} + interval = math.ceil(interval * mapper[fractal]) + data_interval = ''.join([str(interval), 'm']) + qualifiers = '{}?range={}&interval={}'.format(symbol, data_range, data_interval) + request = url + qualifiers + logger.info(request) + response = requests.get(request) + response_json = response.json()['chart'] + if response_json['result']: + body = response_json['result'][0] + dt = pd.Series(map(lambda x: arrow.get(x).to('EST').datetime.replace(tzinfo=None), body['timestamp']), name='dt') + df = pd.DataFrame(body['indicators']['quote'][0], index=dt) + df = df.loc[:, ('open', 'high', 'low', 'close', 'volume')] + else: + logger.info("Could not get data from %s", schema) + logger.info(response_json['error']['code']) + logger.info(response_json['error']['description']) + else: + # use pandas data reader + df = get_pandas_data(schema, subschema, symbol, intraday_data, data_fractal, + from_date, to_date, lookback_period) + + return df + + +# +# Data Dispatch Tables +# + +data_dispatch_table = {'google' : get_google_data, + 'iex' : get_iex_data, + 'pandas' : get_pandas_data, + 'quandl' : get_quandl_data, + 'yahoo' : get_yahoo_data} + + # # Function get_market_data # -def get_market_data(model, group, lookback_period, - data_fractal, intraday_data=False): +def get_market_data(model, market_specs, group, lookback_period, intraday_data=False): r"""Get data from an external feed. Parameters ---------- model : alphapy.Model The model object describing the data. + market_specs : dict + The specifications for controlling the MarketFlow pipeline. group : alphapy.Group The group of symbols. lookback_period : int The number of periods of data to retrieve. - data_fractal : str - Pandas offset alias. intraday_data : bool If True, then get intraday data. @@ -524,6 +761,11 @@ def get_market_data(model, group, lookback_period, """ + # Unpack market specifications + + data_fractal = market_specs['data_fractal'] + subschema = market_specs['subschema'] + # Unpack model specifications directory = model.specs['directory'] @@ -540,42 +782,54 @@ def get_market_data(model, group, lookback_period, if intraday_data: # intraday data (date and time) - logger.info("Getting Intraday Data [%s] from %s", data_fractal, schema) + logger.info("%s Intraday Data [%s] for %d periods", + schema, data_fractal, lookback_period) index_column = 'datetime' else: # daily data or higher (date only) - logger.info("Getting Daily Data [%s] from %s", data_fractal, schema) + logger.info("%s Daily Data [%s] for %d periods", + schema, data_fractal, lookback_period) index_column = 'date' # Get the data from the relevant feed data_dir = SSEP.join([directory, 'data']) - pandas_data = any(substring in schema for substring in PD_WEB_DATA_FEEDS) n_periods = 0 resample_data = True if fractal != data_fractal else False - df = None + + # Date Arithmetic + to_date = pd.to_datetime('today') from_date = to_date - pd.to_timedelta(lookback_period, unit='d') + to_date = to_date.strftime('%Y-%m-%d') + from_date = from_date.strftime('%Y-%m-%d') + + # Get the data from the specified data feed - for item in group.members: - logger.info("Getting %s data for last %d days", item, lookback_period) + df = pd.DataFrame() + for symbol in group.members: + logger.info("Getting %s data from %s to %s", + symbol.upper(), from_date, to_date) # Locate the data source if schema == 'data': # local intraday or daily dspace = Space(gspace.subject, gspace.schema, data_fractal) - fname = frame_name(item.lower(), dspace) + fname = frame_name(symbol.lower(), dspace) df = read_frame(data_dir, fname, extension, separator) - elif schema == 'google' and intraday_data: - # intraday only - df = get_google_data(item, lookback_period, data_fractal) - elif pandas_data: - # daily only - df = get_pandas_data(schema, item, lookback_period) + elif schema in data_dispatch_table.keys(): + df = data_dispatch_table[schema](schema, + subschema, + symbol, + intraday_data, + data_fractal, + from_date, + to_date, + lookback_period) else: logger.error("Unsupported Data Source: %s", schema) # Now that we have content, standardize the data - if df is not None and not df.empty: - logger.info("%d data points from %s to %s", len(df), from_date, to_date) + if not df.empty: + logger.info("Rows: %d [%s]", len(df), data_fractal) # convert data to canonical form df = convert_data(df, index_column, intraday_data) # resample data and forward fill any NA values @@ -592,15 +846,15 @@ def get_market_data(model, group, lookback_period, if intraday_data: df = enhance_intraday_data(df) # allocate global Frame - newf = Frame(item.lower(), gspace, df) + newf = Frame(symbol.lower(), gspace, df) if newf is None: - logger.error("Could not allocate Frame for: %s", item) + logger.error("Could not allocate Frame for: %s", symbol.upper()) # calculate maximum number of periods df_len = len(df) if df_len > n_periods: n_periods = df_len else: - logger.info("No DataFrame for %s", item) + logger.info("No DataFrame for %s", symbol.upper()) # The number of periods actually retrieved return n_periods diff --git a/alphapy/estimators.py b/alphapy/estimators.py index 2b8e29b..1b10ee2 100644 --- a/alphapy/estimators.py +++ b/alphapy/estimators.py @@ -200,7 +200,7 @@ def get_algos_config(cfg_dir): full_path = SSEP.join([cfg_dir, 'algos.yml']) with open(full_path, 'r') as ymlfile: - specs = yaml.load(ymlfile) + specs = yaml.load(ymlfile, Loader=yaml.FullLoader) # Ensure each algorithm has required keys diff --git a/alphapy/examples/NCAAB/config/model.yml b/alphapy/examples/NCAAB/config/model.yml index f1352be..a541cc6 100644 --- a/alphapy/examples/NCAAB/config/model.yml +++ b/alphapy/examples/NCAAB/config/model.yml @@ -5,10 +5,9 @@ project: submit_probas : False data: - drop : ['Unnamed: 0', 'index', 'season', 'date', 'home.team', 'away.team', - 'home.score', 'away.score', 'total_points', 'point_margin_game', - 'won_on_points', 'lost_on_points', 'cover_margin_game', - 'lost_on_spread', 'overunder_margin', 'over', 'under'] + drop : ['index', 'season', 'date', 'home.team', 'away.team', 'home.score', 'away.score', + 'total_points', 'point_margin_game', 'won_on_points', 'lost_on_points', + 'cover_margin_game', 'lost_on_spread', 'overunder_margin', 'over', 'under'] features : '*' sampling : option : False diff --git a/alphapy/examples/Trading Model/config/market.yml b/alphapy/examples/Trading Model/config/market.yml index ba0ecd2..06b81a7 100644 --- a/alphapy/examples/Trading Model/config/market.yml +++ b/alphapy/examples/Trading Model/config/market.yml @@ -7,7 +7,10 @@ market: lag_period : 1 leaders : ['gap', 'gapbadown', 'gapbaup', 'gapdown', 'gapup'] predict_history : 100 - schema : iex + schema : yahoo + subschema : + api_key_name : + api_key : subject : stock target_group : test diff --git a/alphapy/examples/Trading System/A Trading System.ipynb b/alphapy/examples/Trading System/A Trading System.ipynb index dd16923..afc90c2 100644 --- a/alphapy/examples/Trading System/A Trading System.ipynb +++ b/alphapy/examples/Trading System/A Trading System.ipynb @@ -2,9 +2,29 @@ "cells": [ { "cell_type": "code", - "execution_count": null, + "execution_count": 2, "metadata": {}, - "outputs": [], + "outputs": [ + { + "ename": "ImportError", + "evalue": "cannot import name 'is_list_like'", + "output_type": "error", + "traceback": [ + "\u001b[0;31m---------------------------------------------------------------------------\u001b[0m", + "\u001b[0;31mImportError\u001b[0m Traceback (most recent call last)", + "\u001b[0;32m\u001b[0m in \u001b[0;36m\u001b[0;34m\u001b[0m\n\u001b[1;32m 1\u001b[0m \u001b[0mget_ipython\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mrun_line_magic\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m'matplotlib'\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0;34m'inline'\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 2\u001b[0m \u001b[0;32mimport\u001b[0m \u001b[0mpandas\u001b[0m \u001b[0;32mas\u001b[0m \u001b[0mpd\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m----> 3\u001b[0;31m \u001b[0;32mimport\u001b[0m \u001b[0mpyfolio\u001b[0m \u001b[0;32mas\u001b[0m \u001b[0mpf\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m", + "\u001b[0;32m/anaconda3/lib/python3.6/site-packages/pyfolio/__init__.py\u001b[0m in \u001b[0;36m\u001b[0;34m\u001b[0m\n\u001b[1;32m 1\u001b[0m \u001b[0;32mimport\u001b[0m \u001b[0mwarnings\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 2\u001b[0m \u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m----> 3\u001b[0;31m \u001b[0;32mfrom\u001b[0m \u001b[0;34m.\u001b[0m \u001b[0;32mimport\u001b[0m \u001b[0mutils\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m\u001b[1;32m 4\u001b[0m \u001b[0;32mfrom\u001b[0m \u001b[0;34m.\u001b[0m \u001b[0;32mimport\u001b[0m \u001b[0mtimeseries\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 5\u001b[0m \u001b[0;32mfrom\u001b[0m \u001b[0;34m.\u001b[0m \u001b[0;32mimport\u001b[0m \u001b[0mpos\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n", + "\u001b[0;32m/anaconda3/lib/python3.6/site-packages/pyfolio/utils.py\u001b[0m in \u001b[0;36m\u001b[0;34m\u001b[0m\n\u001b[1;32m 22\u001b[0m \u001b[0;32mfrom\u001b[0m \u001b[0mIPython\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mdisplay\u001b[0m \u001b[0;32mimport\u001b[0m \u001b[0mdisplay\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 23\u001b[0m \u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m---> 24\u001b[0;31m \u001b[0;32mimport\u001b[0m \u001b[0mempyrical\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mutils\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m\u001b[1;32m 25\u001b[0m \u001b[0;32mfrom\u001b[0m \u001b[0mos\u001b[0m \u001b[0;32mimport\u001b[0m \u001b[0menviron\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 26\u001b[0m \u001b[0;32mfrom\u001b[0m \u001b[0;34m.\u001b[0m\u001b[0mdeprecate\u001b[0m \u001b[0;32mimport\u001b[0m \u001b[0mdeprecated\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n", + "\u001b[0;32m/anaconda3/lib/python3.6/site-packages/empyrical/__init__.py\u001b[0m in \u001b[0;36m\u001b[0;34m\u001b[0m\n\u001b[1;32m 19\u001b[0m \u001b[0;32mdel\u001b[0m \u001b[0mget_versions\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 20\u001b[0m \u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m---> 21\u001b[0;31m from .stats import (\n\u001b[0m\u001b[1;32m 22\u001b[0m \u001b[0maggregate_returns\u001b[0m\u001b[0;34m,\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 23\u001b[0m \u001b[0malpha\u001b[0m\u001b[0;34m,\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n", + "\u001b[0;32m/anaconda3/lib/python3.6/site-packages/empyrical/stats.py\u001b[0m in \u001b[0;36m\u001b[0;34m\u001b[0m\n\u001b[1;32m 21\u001b[0m \u001b[0;32mfrom\u001b[0m \u001b[0msix\u001b[0m \u001b[0;32mimport\u001b[0m \u001b[0miteritems\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 22\u001b[0m \u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m---> 23\u001b[0;31m \u001b[0;32mfrom\u001b[0m \u001b[0;34m.\u001b[0m\u001b[0mutils\u001b[0m \u001b[0;32mimport\u001b[0m \u001b[0mnanmean\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mnanstd\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mnanmin\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mup\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mdown\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mroll\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mrolling_window\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m\u001b[1;32m 24\u001b[0m \u001b[0;32mfrom\u001b[0m \u001b[0;34m.\u001b[0m\u001b[0mperiods\u001b[0m \u001b[0;32mimport\u001b[0m \u001b[0mANNUALIZATION_FACTORS\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mAPPROX_BDAYS_PER_YEAR\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 25\u001b[0m \u001b[0;32mfrom\u001b[0m \u001b[0;34m.\u001b[0m\u001b[0mperiods\u001b[0m \u001b[0;32mimport\u001b[0m \u001b[0mDAILY\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mWEEKLY\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mMONTHLY\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mYEARLY\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n", + "\u001b[0;32m/anaconda3/lib/python3.6/site-packages/empyrical/utils.py\u001b[0m in \u001b[0;36m\u001b[0;34m\u001b[0m\n\u001b[1;32m 24\u001b[0m \u001b[0;32mimport\u001b[0m \u001b[0mpandas\u001b[0m \u001b[0;32mas\u001b[0m \u001b[0mpd\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 25\u001b[0m \u001b[0;32mfrom\u001b[0m \u001b[0mpandas\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mtseries\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0moffsets\u001b[0m \u001b[0;32mimport\u001b[0m \u001b[0mBDay\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m---> 26\u001b[0;31m \u001b[0;32mfrom\u001b[0m \u001b[0mpandas_datareader\u001b[0m \u001b[0;32mimport\u001b[0m \u001b[0mdata\u001b[0m \u001b[0;32mas\u001b[0m \u001b[0mweb\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m\u001b[1;32m 27\u001b[0m \u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 28\u001b[0m \u001b[0;32mtry\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n", + "\u001b[0;32m/anaconda3/lib/python3.6/site-packages/pandas_datareader/__init__.py\u001b[0m in \u001b[0;36m\u001b[0;34m\u001b[0m\n\u001b[1;32m 1\u001b[0m \u001b[0;32mfrom\u001b[0m \u001b[0;34m.\u001b[0m\u001b[0m_version\u001b[0m \u001b[0;32mimport\u001b[0m \u001b[0mget_versions\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m----> 2\u001b[0;31m from .data import (DataReader, Options, get_components_yahoo,\n\u001b[0m\u001b[1;32m 3\u001b[0m \u001b[0mget_dailysummary_iex\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mget_data_enigma\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mget_data_famafrench\u001b[0m\u001b[0;34m,\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 4\u001b[0m \u001b[0mget_data_fred\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mget_data_google\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mget_data_moex\u001b[0m\u001b[0;34m,\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 5\u001b[0m \u001b[0mget_data_morningstar\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mget_data_quandl\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mget_data_stooq\u001b[0m\u001b[0;34m,\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n", + "\u001b[0;32m/anaconda3/lib/python3.6/site-packages/pandas_datareader/data.py\u001b[0m in \u001b[0;36m\u001b[0;34m\u001b[0m\n\u001b[1;32m 12\u001b[0m \u001b[0mImmediateDeprecationError\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 13\u001b[0m \u001b[0;32mfrom\u001b[0m \u001b[0mpandas_datareader\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mfamafrench\u001b[0m \u001b[0;32mimport\u001b[0m \u001b[0mFamaFrenchReader\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m---> 14\u001b[0;31m \u001b[0;32mfrom\u001b[0m \u001b[0mpandas_datareader\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mfred\u001b[0m \u001b[0;32mimport\u001b[0m \u001b[0mFredReader\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m\u001b[1;32m 15\u001b[0m \u001b[0;32mfrom\u001b[0m \u001b[0mpandas_datareader\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mgoogle\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mdaily\u001b[0m \u001b[0;32mimport\u001b[0m \u001b[0mGoogleDailyReader\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 16\u001b[0m \u001b[0;32mfrom\u001b[0m \u001b[0mpandas_datareader\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mgoogle\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0moptions\u001b[0m \u001b[0;32mimport\u001b[0m \u001b[0mOptions\u001b[0m \u001b[0;32mas\u001b[0m \u001b[0mGoogleOptions\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n", + "\u001b[0;32m/anaconda3/lib/python3.6/site-packages/pandas_datareader/fred.py\u001b[0m in \u001b[0;36m\u001b[0;34m\u001b[0m\n\u001b[0;32m----> 1\u001b[0;31m \u001b[0;32mfrom\u001b[0m \u001b[0mpandas\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mcore\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mcommon\u001b[0m \u001b[0;32mimport\u001b[0m \u001b[0mis_list_like\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m\u001b[1;32m 2\u001b[0m \u001b[0;32mfrom\u001b[0m \u001b[0mpandas\u001b[0m \u001b[0;32mimport\u001b[0m \u001b[0mconcat\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mread_csv\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 3\u001b[0m \u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 4\u001b[0m \u001b[0;32mfrom\u001b[0m \u001b[0mpandas_datareader\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mbase\u001b[0m \u001b[0;32mimport\u001b[0m \u001b[0m_BaseReader\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 5\u001b[0m \u001b[0;34m\u001b[0m\u001b[0m\n", + "\u001b[0;31mImportError\u001b[0m: cannot import name 'is_list_like'" + ] + } + ], "source": [ "%matplotlib inline\n", "import pandas as pd\n", diff --git a/alphapy/examples/Trading System/config/market.yml b/alphapy/examples/Trading System/config/market.yml index dbdcd64..f4b39d4 100644 --- a/alphapy/examples/Trading System/config/market.yml +++ b/alphapy/examples/Trading System/config/market.yml @@ -7,7 +7,10 @@ market: lag_period : 1 leaders : [] predict_history : 50 - schema : iex + schema : yahoo + subschema : + api_key_name : + api_key : subject : stock target_group : faang diff --git a/alphapy/features.py b/alphapy/features.py index f323daf..de0f03d 100644 --- a/alphapy/features.py +++ b/alphapy/features.py @@ -1308,7 +1308,7 @@ def create_features(model, X): dtype = X[fc].dtypes nunique = len(X[fc].unique()) # standard processing of numerical, categorical, and text features - if fc in factors: + if factors and fc in factors: features = get_factors(model, X, fnum, fc, nunique, dtype, encoder, rounding, sentinel) elif dtype == 'float64' or dtype == 'int64' or dtype == 'bool': @@ -1600,14 +1600,15 @@ def drop_features(X, drop): """ drop_cols = [] - for d in drop: - for col in X.columns: - if col.split(LOFF)[0] == d: - drop_cols.append(col) - logger.info("Dropping Features: %s", drop_cols) - logger.info("Original Feature Count : %d", X.shape[1]) - X.drop(drop_cols, axis=1, inplace=True, errors='ignore') - logger.info("Reduced Feature Count : %d", X.shape[1]) + if drop: + for d in drop: + for col in X.columns: + if col.split(LOFF)[0] == d: + drop_cols.append(col) + logger.info("Dropping Features: %s", drop_cols) + logger.info("Original Feature Count : %d", X.shape[1]) + X.drop(drop_cols, axis=1, inplace=True, errors='ignore') + logger.info("Reduced Feature Count : %d", X.shape[1]) return X diff --git a/alphapy/frame.py b/alphapy/frame.py index 564a114..0b09c59 100644 --- a/alphapy/frame.py +++ b/alphapy/frame.py @@ -163,9 +163,9 @@ def read_frame(directory, filename, extension, separator, logger.info("Loading data from %s", file_all) try: df = pd.read_csv(file_all, sep=separator, index_col=index_col, - squeeze=squeeze) + squeeze=squeeze, low_memory=False) except: - df = None + df = pd.DataFrame() logger.info("Could not find or access %s", file_all) return df @@ -260,7 +260,7 @@ def load_frames(group, directory, extension, separator, splits=False): logger.info("Load Data Frame %s from file", fname) df = read_frame(directory, fname, extension, separator) # add this frame to the consolidated frame list - if df is not None and not df.empty: + if not df.empty: # set the name df.insert(0, TAG_ID, gn) all_frames.append(df) @@ -270,7 +270,7 @@ def load_frames(group, directory, extension, separator, splits=False): # no splits, so use data from consolidated files fname = frame_name(gname, gspace) df = read_frame(directory, fname, extension, separator) - if df is not None and not df.empty: + if not df.empty: all_frames.append(df) return all_frames diff --git a/alphapy/globals.py b/alphapy/globals.py index 3eb23d3..2dfbcab 100644 --- a/alphapy/globals.py +++ b/alphapy/globals.py @@ -4,7 +4,7 @@ # Module : globals # Created : July 11, 2013 # -# Copyright 2017 ScottFree Analytics LLC +# Copyright 2019 ScottFree Analytics LLC # Mark Conway & Robert D. Scott II # # Licensed under the Apache License, Version 2.0 (the "License"); @@ -74,13 +74,6 @@ PD_INTRADAY_OFFSETS = ['H', 'T', 'min', 'S', 'L', 'ms', 'U', 'us', 'N'] -# -# Pandas Web Reader Feeds -# - -PD_WEB_DATA_FEEDS = ['google', 'iex', 'quandl', 'yahoo'] - - # # Encoder Types # diff --git a/alphapy/market_flow.py b/alphapy/market_flow.py index 7aad58e..01a6d7c 100644 --- a/alphapy/market_flow.py +++ b/alphapy/market_flow.py @@ -4,7 +4,7 @@ # Module : market_flow # Created : July 11, 2013 # -# Copyright 2017 ScottFree Analytics LLC +# Copyright 2019 ScottFree Analytics LLC # Mark Conway & Robert D. Scott II # # Licensed under the Apache License, Version 2.0 (the "License"); @@ -86,7 +86,7 @@ def get_market_config(): full_path = SSEP.join([PSEP, 'config', 'market.yml']) with open(full_path, 'r') as ymlfile: - cfg = yaml.load(ymlfile) + cfg = yaml.load(ymlfile, Loader=yaml.FullLoader) # Store configuration parameters in dictionary @@ -115,12 +115,19 @@ def get_market_config(): specs['leaders'] = cfg['market']['leaders'] specs['predict_history'] = cfg['market']['predict_history'] specs['schema'] = cfg['market']['schema'] + specs['subschema'] = cfg['market']['subschema'] + specs['api_key_name'] = cfg['market']['api_key_name'] + specs['api_key'] = cfg['market']['api_key'] specs['subject'] = cfg['market']['subject'] specs['target_group'] = cfg['market']['target_group'] + # Set API Key environment variable + if specs['api_key']: + os.environ[specs['api_key_name']] = specs['api_key'] + # Create the subject/schema/fractal namespace - sspecs = [specs['subject'], specs['schema'], specs['fractal']] + sspecs = [specs['subject'], specs['schema'], specs['fractal']] space = Space(*sspecs) # Section: features @@ -186,6 +193,8 @@ def get_market_config(): # Log the stock parameters logger.info('MARKET PARAMETERS:') + logger.info('api_key = %s', specs['api_key']) + logger.info('api_key_name = %s', specs['api_key_name']) logger.info('create_model = %r', specs['create_model']) logger.info('data_fractal = %s', specs['data_fractal']) logger.info('data_history = %d', specs['data_history']) @@ -197,6 +206,7 @@ def get_market_config(): logger.info('predict_history = %s', specs['predict_history']) logger.info('schema = %s', specs['schema']) logger.info('subject = %s', specs['subject']) + logger.info('subschema = %s', specs['subschema']) logger.info('system = %s', specs['system']) logger.info('target_group = %s', specs['target_group']) @@ -243,7 +253,6 @@ def market_pipeline(model, market_specs): # Get market specifications create_model = market_specs['create_model'] - data_fractal = market_specs['data_fractal'] data_history = market_specs['data_history'] features = market_specs['features'] forecast_period = market_specs['forecast_period'] @@ -267,7 +276,7 @@ def market_pipeline(model, market_specs): # predict_history resets to the actual history obtained. lookback = predict_history if predict_mode else data_history - npoints = get_market_data(model, group, lookback, data_fractal, intraday) + npoints = get_market_data(model, market_specs, group, lookback, intraday) if npoints > 0: logger.info("Number of Data Points: %d", npoints) else: diff --git a/alphapy/model.py b/alphapy/model.py index d0d6401..a7865dc 100644 --- a/alphapy/model.py +++ b/alphapy/model.py @@ -43,12 +43,12 @@ from copy import copy from datetime import datetime +import joblib from keras.models import load_model import logging import numpy as np import pandas as pd from sklearn.calibration import CalibratedClassifierCV -from sklearn.externals import joblib from sklearn.linear_model import LogisticRegression from sklearn.linear_model import RidgeCV from sklearn.metrics import accuracy_score @@ -214,7 +214,7 @@ def get_model_config(): full_path = SSEP.join([PSEP, 'config', 'model.yml']) with open(full_path, 'r') as ymlfile: - cfg = yaml.load(ymlfile) + cfg = yaml.load(ymlfile, Loader=yaml.FullLoader) # Store configuration parameters in dictionary @@ -654,7 +654,6 @@ def first_fit(model, algo, est): cv_folds = model.specs['cv_folds'] esr = model.specs['esr'] - model_type = model.specs['model_type'] n_jobs = model.specs['n_jobs'] scorer = model.specs['scorer'] seed = model.specs['seed'] @@ -1188,7 +1187,10 @@ def save_predictions(model, tag, partition): output_dir = SSEP.join([directory, 'output']) # Read the prediction frame - pf = read_frame(input_dir, datasets[partition], extension, separator) + file_spec = ''.join([datasets[partition], '*']) + file_name = most_recent_file(input_dir, file_spec) + file_name = file_name.split(SSEP)[-1].split(PSEP)[0] + pf = read_frame(input_dir, file_name, extension, separator) # Cull records before the prediction date diff --git a/alphapy/plots.py b/alphapy/plots.py index ee1a44b..2fc2ffd 100644 --- a/alphapy/plots.py +++ b/alphapy/plots.py @@ -86,6 +86,7 @@ from sklearn.model_selection import StratifiedKFold from sklearn.model_selection import train_test_split from sklearn.model_selection import validation_curve +from sklearn.utils.multiclass import unique_labels # @@ -269,7 +270,6 @@ def write_plot(vizlib, plot, plot_type, tag, directory=None): file_all = SSEP.join([directory, file_only]) logger.info("Writing plot to %s", file_all) if vizlib == 'matplotlib': - plot.tight_layout() plot.savefig(file_all) elif vizlib == 'seaborn': plot.savefig(file_all) @@ -622,45 +622,63 @@ def plot_confusion_matrix(model, partition): return None # Get X, Y for correct partition. - X, y = get_partition_data(model, partition) + # Plot Parameters + np.set_printoptions(precision=2) + cmap = plt.cm.Blues + fmt = '.2f' + + # Generate a Confusion Matrix for each algorithm + for algo in model.algolist: logger.info("Confusion Matrix for Algorithm: %s", algo) + # get predictions for this partition y_pred = model.preds[(algo, partition)] + # compute confusion matrix cm = confusion_matrix(y, y_pred) logger.info('Confusion Matrix:') logger.info('%s', cm) + + # normalize confusion matrix + cm = cm.astype('float') / cm.sum(axis=1)[:, np.newaxis] + # initialize plot - np.set_printoptions(precision=2) - plt.style.use('classic') - plt.figure() - # plot the confusion matrix - cmap = plt.cm.Blues - plt.imshow(cm, interpolation='nearest', cmap=cmap) + _, ax = plt.subplots() + + # set the title of the confusion matrix title = BSEP.join([algo, "Confusion Matrix [", pstring, "]"]) plt.title(title) - plt.colorbar() - # set up x and y axes - y_values, y_counts = np.unique(y, return_counts=True) - tick_marks = np.arange(len(y_values)) - plt.xticks(tick_marks, y_values, rotation=45) - plt.yticks(tick_marks, y_values) - # normalize confusion matrix - cmn = cm.astype('float') / cm.sum(axis=1)[:, np.newaxis] - # place text in square of confusion matrix + + # only use the labels that appear in the data + classes = unique_labels(y, y_pred) + + # show all ticks + ax.set(xticks=np.arange(cm.shape[1]), + yticks=np.arange(cm.shape[0]), + xticklabels=classes, yticklabels=classes, + title=title, + ylabel='True Label', + xlabel='Predicted Label') + + # rotate the tick labels and set their alignment + plt.setp(ax.get_xticklabels(), rotation=45, ha="right", + rotation_mode="anchor") + + # loop over data dimensions and create text annotations thresh = (cm.max() + cm.min()) / 2.0 - for i, j in product(list(range(cm.shape[0])), list(range(cm.shape[1]))): - cmr = round(cmn[i, j], 3) - plt.text(j, i, cmr, - horizontalalignment="center", - color="white" if cm[i, j] > thresh else "black") - # labels - plt.tight_layout() - plt.ylabel('True Label') - plt.xlabel('Predicted Label') + for i in range(cm.shape[0]): + for j in range(cm.shape[1]): + ax.text(j, i, format(cm[i, j], fmt), + ha="center", va="center", + color="white" if cm[i, j] > thresh else "black") + + # show the color bar + im = ax.imshow(cm, interpolation='nearest', cmap=cmap) + ax.figure.colorbar(im, ax=ax) + # save the chart tag = USEP.join([pstring, algo]) write_plot('matplotlib', plt, 'confusion', tag, plot_dir) diff --git a/alphapy/sport_flow.py b/alphapy/sport_flow.py index c35a464..7ad59f3 100644 --- a/alphapy/sport_flow.py +++ b/alphapy/sport_flow.py @@ -157,7 +157,7 @@ def get_sport_config(): full_path = SSEP.join(['.', 'config', 'sport.yml']) with open(full_path, 'r') as ymlfile: - cfg = yaml.load(ymlfile) + cfg = yaml.load(ymlfile, Loader=yaml.FullLoader) # Store configuration parameters in dictionary diff --git a/docs/conf.py b/docs/conf.py index 3cc165c..0a0a382 100644 --- a/docs/conf.py +++ b/docs/conf.py @@ -62,9 +62,9 @@ # built documents. # # The short X.Y version. -version = '2.3.8' +version = '2.3.9' # The full version, including alpha/beta/rc tags. -release = '2.3.8' +release = '2.3.9' # The language for content autogenerated by Sphinx. Refer to documentation # for a list of supported languages. diff --git a/environment.yml b/environment.yml index 8356502..4ee2778 100644 --- a/environment.yml +++ b/environment.yml @@ -4,20 +4,22 @@ channels: - conda-forge dependencies: -- bokeh>=0.12 -- ipython>=5.0 +- bokeh>=1.3 +- ipython>=7.2 - keras>=2.2 -- matplotlib>=2.0.0 -- numpy>=1.12 -- pandas>=0.22 -- pyyaml>=3.12 -- scikit-learn>=0.20 -- scipy>=1.0 -- seaborn>=0.8 -- tensorflow>=1.12 -- xgboost>=0.71 +- matplotlib>=3.0 +- numpy>=1.17 +- pandas>=0.24 +- pyyaml>=5.0 +- scikit-learn>=0.21 +- scipy>=1.1 +- seaborn>=0.9 +- tensorflow>=1.15 +- xgboost>=0.8 - pip: - - category_encoders>=1.2.0 - - imbalanced-learn>=0.3 - - pandas-datareader>=0.6 - - pyfolio>=0.8 + - arrow>=0.13 + - category_encoders>=2.1 + - iexfinance>=0.4.3 + - imbalanced-learn>=0.5 + - pandas-datareader>=0.8 + - pyfolio>=0.9 diff --git a/setup.py b/setup.py index f2c24b7..be3652f 100644 --- a/setup.py +++ b/setup.py @@ -10,13 +10,13 @@ MAINTAINER_EMAIL = 'scottfree.analytics@scottfreellc.com' URL = "https://github.com/ScottFreeLLC/AlphaPy" LICENSE = "Apache License, Version 2" -VERSION = "2.3.8" +VERSION = "2.3.9" classifiers = ['Development Status :: 4 - Beta', 'Programming Language :: Python', 'Programming Language :: Python :: 3', - 'Programming Language :: Python :: 3.4', - 'Programming Language :: Python :: 3.5', + 'Programming Language :: Python :: 3.6', + 'Programming Language :: Python :: 3.7', 'License :: OSI Approved :: Apache Software License', 'Intended Audience :: Science/Research', 'Topic :: Scientific/Engineering', @@ -24,21 +24,23 @@ 'Operating System :: OS Independent'] install_reqs = [ - 'bokeh>=1.0', - 'category_encoders>=1.3', - 'imbalanced-learn>=0.4.3', + 'arrow>=0.13', + 'bokeh>=1.3', + 'category_encoders>=2.1', + 'iexfinance>=0.4.3', + 'imbalanced-learn>=0.5', 'ipython>=7.2', 'keras>=2.2', 'matplotlib>=3.0', - 'numpy>=1.15', + 'numpy>=1.17', 'pandas>=0.24', - 'pandas-datareader>=0.7', + 'pandas-datareader>=0.8', 'pyfolio>=0.9', - 'pyyaml>=3.12', - 'scikit-learn>=0.20', + 'pyyaml>=5.0', + 'scikit-learn>=0.21', 'scipy>=1.1', 'seaborn>=0.9', - 'tensorflow>=1.13', + 'tensorflow>=1.15', ] if __name__ == "__main__": From 0204271370b25ab35495ff8cfc85b91e62b06915 Mon Sep 17 00:00:00 2001 From: Mark Conway Date: Thu, 2 Jan 2020 16:45:28 -0500 Subject: [PATCH 061/129] Create CODE_OF_CONDUCT.md --- CODE_OF_CONDUCT.md | 76 ++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 76 insertions(+) create mode 100644 CODE_OF_CONDUCT.md diff --git a/CODE_OF_CONDUCT.md b/CODE_OF_CONDUCT.md new file mode 100644 index 0000000..0169ccd --- /dev/null +++ b/CODE_OF_CONDUCT.md @@ -0,0 +1,76 @@ +# Contributor Covenant Code of Conduct + +## Our Pledge + +In the interest of fostering an open and welcoming environment, we as +contributors and maintainers pledge to making participation in our project and +our community a harassment-free experience for everyone, regardless of age, body +size, disability, ethnicity, sex characteristics, gender identity and expression, +level of experience, education, socio-economic status, nationality, personal +appearance, race, religion, or sexual identity and orientation. + +## Our Standards + +Examples of behavior that contributes to creating a positive environment +include: + +* Using welcoming and inclusive language +* Being respectful of differing viewpoints and experiences +* Gracefully accepting constructive criticism +* Focusing on what is best for the community +* Showing empathy towards other community members + +Examples of unacceptable behavior by participants include: + +* The use of sexualized language or imagery and unwelcome sexual attention or + advances +* Trolling, insulting/derogatory comments, and personal or political attacks +* Public or private harassment +* Publishing others' private information, such as a physical or electronic + address, without explicit permission +* Other conduct which could reasonably be considered inappropriate in a + professional setting + +## Our Responsibilities + +Project maintainers are responsible for clarifying the standards of acceptable +behavior and are expected to take appropriate and fair corrective action in +response to any instances of unacceptable behavior. + +Project maintainers have the right and responsibility to remove, edit, or +reject comments, commits, code, wiki edits, issues, and other contributions +that are not aligned to this Code of Conduct, or to ban temporarily or +permanently any contributor for other behaviors that they deem inappropriate, +threatening, offensive, or harmful. + +## Scope + +This Code of Conduct applies both within project spaces and in public spaces +when an individual is representing the project or its community. Examples of +representing a project or community include using an official project e-mail +address, posting via an official social media account, or acting as an appointed +representative at an online or offline event. Representation of a project may be +further defined and clarified by project maintainers. + +## Enforcement + +Instances of abusive, harassing, or otherwise unacceptable behavior may be +reported by contacting the project team at scottfree.analytics@scottfreellc.com. All +complaints will be reviewed and investigated and will result in a response that +is deemed necessary and appropriate to the circumstances. The project team is +obligated to maintain confidentiality with regard to the reporter of an incident. +Further details of specific enforcement policies may be posted separately. + +Project maintainers who do not follow or enforce the Code of Conduct in good +faith may face temporary or permanent repercussions as determined by other +members of the project's leadership. + +## Attribution + +This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4, +available at https://www.contributor-covenant.org/version/1/4/code-of-conduct.html + +[homepage]: https://www.contributor-covenant.org + +For answers to common questions about this code of conduct, see +https://www.contributor-covenant.org/faq From 89b82080db6fc7b12cb432824333acbf1c02b792 Mon Sep 17 00:00:00 2001 From: Mark Conway Date: Sun, 9 Feb 2020 15:49:04 -0500 Subject: [PATCH 062/129] add new encoders such as catboost, target, and woe add new encoders such as catboost, target, and woe --- alphapy/globals.py | 23 +++++++++++++++-------- 1 file changed, 15 insertions(+), 8 deletions(-) diff --git a/alphapy/globals.py b/alphapy/globals.py index 2dfbcab..f473048 100644 --- a/alphapy/globals.py +++ b/alphapy/globals.py @@ -4,7 +4,7 @@ # Module : globals # Created : July 11, 2013 # -# Copyright 2019 ScottFree Analytics LLC +# Copyright 2020 ScottFree Analytics LLC # Mark Conway & Robert D. Scott II # # Licensed under the Apache License, Version 2.0 (the "License"); @@ -90,13 +90,20 @@ class Encoders(Enum): """ backdiff = 1 - binary = 2 - factorize = 3 - helmert = 4 - onehot = 5 - ordinal = 6 - polynomial = 7 - sumcont = 8 + basen = 2 + binary = 3 + catboost = 4 + hashing = 5 + helmert = 6 + jstein = 7 + leaveone = 8 + mestimate = 9 + onehot = 10 + ordinal = 11 + polynomial = 12 + sum = 13 + target = 14 + woe = 15 # From eec12d84ed2589ac988e2841dbc5ebb6cb5896fe Mon Sep 17 00:00:00 2001 From: Mark Conway Date: Sun, 9 Feb 2020 15:52:50 -0500 Subject: [PATCH 063/129] feature names and refactor imputation feature names and refactor imputation --- alphapy/features.py | 373 +++++++++++++++++++++++--------------------- 1 file changed, 198 insertions(+), 175 deletions(-) diff --git a/alphapy/features.py b/alphapy/features.py index de0f03d..38ae4b6 100644 --- a/alphapy/features.py +++ b/alphapy/features.py @@ -4,7 +4,7 @@ # Module : features # Created : July 11, 2013 # -# Copyright 2017 ScottFree Analytics LLC +# Copyright 2020 ScottFree Analytics LLC # Mark Conway & Robert D. Scott II # # Licensed under the Apache License, Version 2.0 (the "License"); @@ -36,7 +36,7 @@ import category_encoders as ce from importlib import import_module -from itertools import groupby +import itertools import logging import math import numpy as np @@ -47,8 +47,7 @@ import scipy.stats as sps from sklearn.cluster import MiniBatchKMeans from sklearn.decomposition import PCA -from sklearn.feature_extraction.text import CountVectorizer -from sklearn.feature_extraction.text import TfidfTransformer +from sklearn.feature_extraction.text import TfidfVectorizer from sklearn.feature_selection import chi2 from sklearn.feature_selection import f_classif from sklearn.feature_selection import f_regression @@ -58,9 +57,9 @@ from sklearn.feature_selection import SelectKBest from sklearn.feature_selection import SelectPercentile from sklearn.feature_selection import VarianceThreshold +from sklearn.impute import SimpleImputer from sklearn.manifold import Isomap from sklearn.manifold import TSNE -from sklearn.preprocessing import Imputer from sklearn.preprocessing import MinMaxScaler from sklearn.preprocessing import PolynomialFeatures from sklearn.preprocessing import StandardScaler @@ -87,6 +86,27 @@ 'SelectFwe' : SelectFwe} +# +# Define Encoder map +# + +encoder_map = {Encoders.backdiff : ce.BackwardDifferenceEncoder, + Encoders.basen : ce.BaseNEncoder, + Encoders.binary : ce.BinaryEncoder, + Encoders.catboost : ce.CatBoostEncoder, + Encoders.hashing : ce.HashingEncoder, + Encoders.helmert : ce.HelmertEncoder, + Encoders.jstein : ce.JamesSteinEncoder, + Encoders.leaveone : ce.LeaveOneOutEncoder, + Encoders.mestimate : ce.MEstimateEncoder, + Encoders.onehot : ce.OneHotEncoder, + Encoders.ordinal : ce.OrdinalEncoder, + Encoders.polynomial : ce.PolynomialEncoder, + Encoders.sum : ce.SumEncoder, + Encoders.target : ce.TargetEncoder, + Encoders.woe : ce.WOEEncoder} + + # # Function rtotal # @@ -139,7 +159,7 @@ def runs(vec): >>> vec.rolling(window=20).apply(runs) """ - runs_value = len(list(groupby(vec))) + runs_value = len(list(itertools.groupby(vec))) return runs_value @@ -166,7 +186,7 @@ def streak(vec): >>> vec.rolling(window=20).apply(streak) """ - latest_streak = [len(list(g)) for k, g in groupby(vec)][-1] + latest_streak = [len(list(g)) for k, g in itertools.groupby(vec)][-1] return latest_streak @@ -363,44 +383,6 @@ def texplode(f, c): return dummies -# -# Function cvectorize -# - -def cvectorize(f, c, n): - r"""Use the Count Vectorizer and TF-IDF Transformer. - - Parameters - ---------- - f : pandas.DataFrame - Dataframe containing the column ``c``. - c : str - Name of the text column in the dataframe ``f``. - n : int - The number of n-grams. - - Returns - ------- - new_features : sparse matrix - The transformed features. - - References - ---------- - To use count vectorization and TF-IDF, you can find more - information here [TFE]_. - - .. [TFE] http://scikit-learn.org/stable/modules/feature_extraction.html#text-feature-extraction - - """ - fc = f[c] - fc.fillna(BSEP, inplace=True) - cvect = CountVectorizer(ngram_range=[1, n], analyzer='char') - cfeat = cvect.fit_transform(fc) - tfidf_transformer = TfidfTransformer() - new_features = tfidf_transformer.fit_transform(cfeat).toarray() - return new_features - - # # Function apply_treatment # @@ -521,15 +503,15 @@ def apply_treatments(model, X): # Function impute_values # -def impute_values(features, dt, sentinel): +def impute_values(feature, dt, sentinel): r"""Impute values for a given data type. The *median* strategy is applied for floating point values, and the *most frequent* strategy is applied for integer or Boolean values. Parameters ---------- - features : pandas.DataFrame - Dataframe containing the features for imputation. + feature : pandas.Series or numpy.array + The feature for imputation. dt : str The values ``'float64'``, ``'int64'``, or ``'bool'``. sentinel : float @@ -537,8 +519,8 @@ def impute_values(features, dt, sentinel): Returns ------- - imputed_features : numpy array - The features after imputation. + imputed : numpy.array + The feature after imputation. Raises ------ @@ -552,24 +534,30 @@ def impute_values(features, dt, sentinel): .. [IMP] http://scikit-learn.org/stable/modules/preprocessing.html#imputation """ + try: - nfeatures = features.shape[1] + # for pandas series + feature = feature.values.reshape(-1, 1) except: - features = features.values.reshape(-1, 1) + # for numpy array + feature = feature.reshape(-1, 1) + + imp = None if dt == 'float64': - imp = Imputer(missing_values='NaN', strategy='median', axis=0) - elif dt == 'int64' or dt == 'bool': - imp = Imputer(missing_values='NaN', strategy='most_frequent', axis=0) + logger.info(" Imputation for Data Type %s: Median Strategy" % dt) + imp = SimpleImputer(missing_values=np.nan, strategy='median') + elif dt == 'int64': + logger.info(" Imputation for Data Type %s: Most Frequent Strategy" % dt) + imp = SimpleImputer(missing_values=np.nan, strategy='most_frequent') else: - raise TypeError("Data Type %s is invalid for imputation" % dt) - imputed = imp.fit_transform(features) - if imputed.shape[1] == 0: - nans = np.isnan(features) - features[nans] = sentinel - imputed_features = features + logger.info(" Imputation for Data Type %s: Fill Strategy with %d" % (dt, sentinel)) + + if imp: + imputed = imp.fit_transform(feature) else: - imputed_features = imputed - return imputed_features + feature[np.isnan(feature)] = sentinel + imputed = feature + return imputed # @@ -604,6 +592,8 @@ def get_numerical_features(fnum, fname, df, nvalues, dt, ------- new_values : numpy array The set of imputed and transformed features. + new_fnames : list + The new feature name(s) for the numerical variable. """ feature = df[fname] @@ -616,13 +606,16 @@ def get_numerical_features(fnum, fname, df, nvalues, dt, # imputer for float, integer, or boolean data types new_values = impute_values(feature, dt, sentinel) # log-transform any values that do not fit a normal distribution + new_fname = fname if logt and np.all(new_values > 0): - stat, pvalue = sps.normaltest(new_values) + _, pvalue = sps.normaltest(new_values) if pvalue <= plevel: logger.info("Feature %d: %s is not normally distributed [p-value: %f]", fnum, fname, pvalue) new_values = np.log(new_values) - return new_values + else: + new_fname = USEP.join([new_fname, 'log']) + return new_values, [new_fname] # @@ -643,6 +636,8 @@ def get_polynomials(features, poly_degree): ------- poly_features : numpy array The interaction features only. + poly_fnames : list + List of polynomial feature names. References ---------- @@ -655,7 +650,8 @@ def get_polynomials(features, poly_degree): degree=poly_degree, include_bias=False) poly_features = polyf.fit_transform(features) - return poly_features + poly_fnames = polyf.get_feature_names() + return poly_features, poly_fnames # @@ -685,6 +681,8 @@ def get_text_features(fnum, fname, df, nvalues, vectorize, ngrams_max): ------- new_features : numpy array The vectorized or factorized text features. + new_fnames : list + The new feature name(s) for the numerical variable. References ---------- @@ -706,19 +704,20 @@ def get_text_features(fnum, fname, df, nvalues, vectorize, ngrams_max): # vectorization creates many columns, otherwise just factorize if vectorize: logger.info("Feature %d: %s => Attempting Vectorization", fnum, fname) - count_vect = CountVectorizer(ngram_range=[1, ngrams_max]) + vectorizer = TfidfVectorizer(ngram_range=[1, ngrams_max]) try: - count_feature = count_vect.fit_transform(feature) - tfidf_transformer = TfidfTransformer() - new_features = tfidf_transformer.fit_transform(count_feature).todense() + new_features = vectorizer.fit_transform(feature) + new_fnames = vectorizer.get_feature_names() logger.info("Feature %d: %s => Vectorization Succeeded", fnum, fname) except: logger.info("Feature %d: %s => Vectorization Failed", fnum, fname) - new_features, uniques = pd.factorize(feature) + new_features, _ = pd.factorize(feature) + new_fnames = [USEP.join([fname, 'factor'])] else: logger.info("Feature %d: %s => Factorization", fnum, fname) - new_features, uniques = pd.factorize(feature) - return new_features + new_features, _ = pd.factorize(feature) + new_fnames = [USEP.join([fname, 'factor'])] + return new_features, new_fnames # @@ -775,7 +774,6 @@ def create_crosstabs(model): # Extract model parameters factors = model.specs['factors'] - target_value = model.specs['target_value'] # Iterate through columns, dispatching and transforming each feature. @@ -796,16 +794,20 @@ def create_crosstabs(model): # Function get_factors # -def get_factors(model, df, fnum, fname, nvalues, dtype, - encoder, rounding, sentinel): +def get_factors(model, X_train, X_test, y_train, fnum, fname, + nvalues, dtype, encoder, rounding, sentinel): r"""Convert the original feature to a factor. Parameters ---------- model : alphapy.Model Model object with the feature specifications. - df : pandas.DataFrame - Dataframe containing the column ``fname``. + X_train : pandas.DataFrame + Training dataframe containing the column ``fname``. + X_test : pandas.DataFrame + Testing dataframe containing the column ``fname``. + y_train : pandas.Series + Training series for target variable. fnum : int Feature number, strictly for logging purposes fname : str @@ -825,6 +827,8 @@ def get_factors(model, df, fnum, fname, nvalues, dtype, ------- all_features : numpy array The features that have been transformed to factors. + all_fnames : list + The feature names for the encodings. """ @@ -832,66 +836,40 @@ def get_factors(model, df, fnum, fname, nvalues, dtype, fnum, fname, dtype, nvalues) logger.info("Encoding: %s", encoder) - # Extract model data - - feature_map = model.feature_map - model_type = model.specs['model_type'] - target_value = model.specs['target_value'] - # get feature - feature = df[fname] + feature_train = X_train[fname] + feature_test = X_test[fname] # convert float to factor if dtype == 'float64': logger.info("Rounding: %d", rounding) - feature = feature.apply(float_factor, args=[rounding]) + feature_train = feature_train.apply(float_factor, args=[rounding]) + feature_test = feature_test.apply(float_factor, args=[rounding]) + # create data frames for the feature + df_train = pd.DataFrame(feature_train) + df_test = pd.DataFrame(feature_test) # encoders - pd_features = pd.DataFrame() enc = None - ef = pd.DataFrame(feature) - if encoder == Encoders.factorize: - pd_factors = pd.factorize(feature)[0] - pd_features = pd.DataFrame(pd_factors) - elif encoder == Encoders.onehot: - pd_features = pd.get_dummies(feature) - elif encoder == Encoders.ordinal: - enc = ce.OrdinalEncoder(cols=[fname]) - elif encoder == Encoders.binary: - enc = ce.BinaryEncoder(cols=[fname]) - elif encoder == Encoders.helmert: - enc = ce.HelmertEncoder(cols=[fname]) - elif encoder == Encoders.sumcont: - enc = ce.SumEncoder(cols=[fname]) - elif encoder == Encoders.polynomial: - enc = ce.PolynomialEncoder(cols=[fname]) - elif encoder == Encoders.backdiff: - enc = ce.BackwardDifferenceEncoder(cols=[fname]) - else: + try: + enc = encoder_map[encoder](cols=[fname]) + except: raise ValueError("Unknown Encoder %s" % encoder) - # If encoding worked, calculate target percentages for classifiers. - pd_exists = not pd_features.empty - enc_exists = enc is not None - all_features = None - if pd_exists or enc_exists: - if pd_exists: - all_features = pd_features - elif enc_exists: - all_features = enc.fit_transform(ef, None) - # Calculate target percentages for factors - if (model_type == ModelType.classification and - fname in feature_map['crosstabs']): - # Get the crosstab for this feature - ct = feature_map['crosstabs'][fname] - # map target percentages to the new feature - ct_map = ct.to_dict()[target_value] - ct_feature = df[[fname]].applymap(ct_map.get) - # impute sentinel for any values that could not be mapped - ct_feature.fillna(value=sentinel, inplace=True) - # concatenate all generated features - all_features = np.column_stack((all_features, ct_feature)) - logger.info("Applied target percentages for %s", fname) + # Transform the train and test features. + if enc is not None: + # fit training features + logger.info("Fitting training features for %s", fname) + ftrain = enc.fit_transform(df_train, y_train) + # fit testing features + logger.info("Transforming testing features for %s", fname) + ftest = enc.transform(df_test) + # get feature names + all_fnames = enc.get_feature_names() + # concatenate all generated features + all_features = np.row_stack((ftrain, ftest)) else: - raise RuntimeError("Encoding for feature %s failed" % fname) - return all_features + all_features = None + all_fnames = None + logger.info("Encoding for feature %s failed" % fname) + return all_features, all_fnames # @@ -913,6 +891,8 @@ def create_numpy_features(base_features, sentinel): ------- np_features : numpy array The calculated NumPy features. + np_fnames : list + The NumPy feature names. """ @@ -920,25 +900,27 @@ def create_numpy_features(base_features, sentinel): # Calculate the total, mean, standard deviation, and variance. - logger.info("NumPy Feature: sum") - row_sum = np.sum(base_features, axis=1) - logger.info("NumPy Feature: mean") - row_mean = np.mean(base_features, axis=1) - logger.info("NumPy Feature: standard deviation") - row_std = np.std(base_features, axis=1) - logger.info("NumPy Feature: variance") - row_var = np.var(base_features, axis=1) + np_funcs = {'sum' : np.sum, + 'mean' : np.mean, + 'std' : np.std, + 'var' : np.var} + + features = [] + for k in np_funcs: + logger.info("NumPy Feature: %s", k) + feature = np_funcs[k](base_features, axis=1) + feature = impute_values(feature, 'float64', sentinel) + features.append(feature) - # Impute, scale, and stack all new features. + # Stack and scale the new features. - np_features = np.column_stack((row_sum, row_mean, row_std, row_var)) - np_features = impute_values(np_features, 'float64', sentinel) + np_features = np.column_stack(features) np_features = StandardScaler().fit_transform(np_features) # Return new NumPy features logger.info("NumPy Feature Count : %d", np_features.shape[1]) - return np_features + return np_features, np_funcs.keys() # @@ -960,6 +942,8 @@ def create_scipy_features(base_features, sentinel): ------- sp_features : numpy array The calculated SciPy features. + sp_fnames : list + The SciPy feature names. """ @@ -995,7 +979,16 @@ def create_scipy_features(base_features, sentinel): # Return new SciPy features logger.info("SciPy Feature Count : %d", sp_features.shape[1]) - return sp_features + sp_fnames = ['sp_geometric_mean', + 'sp_kurtosis', + 'sp_kurtosis_test', + 'sp_normal_test', + 'sp_skew', + 'sp_skew_test', + 'sp_variation', + 'sp_signal_to_noise', + 'sp_standard_error_of_mean'] + return sp_features, sp_fnames # @@ -1016,6 +1009,8 @@ def create_clusters(features, model): ------- cfeatures : numpy array The calculated clusters. + cnames : list + The cluster feature names. References ---------- @@ -1032,7 +1027,6 @@ def create_clusters(features, model): cluster_inc = model.specs['cluster_inc'] cluster_max = model.specs['cluster_max'] cluster_min = model.specs['cluster_min'] - n_jobs = model.specs['n_jobs'] seed = model.specs['seed'] # Log model parameters @@ -1044,6 +1038,7 @@ def create_clusters(features, model): # Generate clustering features cfeatures = np.zeros((features.shape[0], 1)) + cnames = [] for i in range(cluster_min, cluster_max+1, cluster_inc): logger.info("k = %d", i) km = MiniBatchKMeans(n_clusters=i, random_state=seed) @@ -1051,12 +1046,13 @@ def create_clusters(features, model): labels = km.predict(features) labels = labels.reshape(-1, 1) cfeatures = np.column_stack((cfeatures, labels)) + cnames.append(USEP.join(['cluster', str(i)])) cfeatures = np.delete(cfeatures, 0, axis=1) # Return new clustering features logger.info("Clustering Feature Count : %d", cfeatures.shape[1]) - return cfeatures + return cfeatures, cnames # @@ -1077,6 +1073,8 @@ def create_pca_features(features, model): ------- pfeatures : numpy array The PCA features. + pnames : list + The PCA feature names. References ---------- @@ -1105,16 +1103,18 @@ def create_pca_features(features, model): # Generate clustering features pfeatures = np.zeros((features.shape[0], 1)) + pnames = [] for i in range(pca_min, pca_max+1, pca_inc): logger.info("n_components = %d", i) X_pca = PCA(n_components=i, whiten=pca_whiten).fit_transform(features) pfeatures = np.column_stack((pfeatures, X_pca)) + pnames.append(USEP.join(['pca', str(i)])) pfeatures = np.delete(pfeatures, 0, axis=1) # Return new clustering features logger.info("PCA Feature Count : %d", pfeatures.shape[1]) - return pfeatures + return pfeatures, pnames # @@ -1135,6 +1135,8 @@ def create_isomap_features(features, model): ------- ifeatures : numpy array The Isomap features. + inames : list + The Isomap feature names. Notes ----- @@ -1168,11 +1170,12 @@ def create_isomap_features(features, model): model = Isomap(n_neighbors=iso_neighbors, n_components=iso_components, n_jobs=n_jobs) ifeatures = model.fit_transform(features) + inames = [USEP.join(['isomap', str(i+1)]) for i in range(iso_components)] # Return new Isomap features logger.info("Isomap Feature Count : %d", ifeatures.shape[1]) - return ifeatures + return ifeatures, inames # @@ -1193,6 +1196,8 @@ def create_tsne_features(features, model): ------- tfeatures : numpy array The t-SNE features. + tnames : list + The t-SNE feature names. References ---------- @@ -1222,18 +1227,19 @@ def create_tsne_features(features, model): model = TSNE(n_components=tsne_components, perplexity=tsne_perplexity, learning_rate=tsne_learn_rate, random_state=seed) tfeatures = model.fit_transform(features) + tnames = [USEP.join(['tsne', str(i+1)]) for i in range(tsne_components)] # Return new T-SNE features logger.info("T-SNE Feature Count : %d", tfeatures.shape[1]) - return tfeatures + return tfeatures, tnames # # Function create_features # -def create_features(model, X): +def create_features(model, X, X_train, X_test, y_train): r"""Create features for the train and test set. Parameters @@ -1242,6 +1248,12 @@ def create_features(model, X): Model object with the feature specifications. X : pandas.DataFrame Combined train and test data. + X_train : pandas.DataFrame + Training data. + X_test : pandas.DataFrame + Testing data. + y_train : pandas.DataFrame + Target variable for training data. Returns ------- @@ -1263,7 +1275,6 @@ def create_features(model, X): factors = model.specs['factors'] isomap = model.specs['isomap'] logtransform = model.specs['logtransform'] - model_type = model.specs['model_type'] ngrams_max = model.specs['ngrams_max'] numpy_flag = model.specs['numpy'] pca = model.specs['pca'] @@ -1273,7 +1284,6 @@ def create_features(model, X): scaler = model.specs['scaler_type'] scipy_flag = model.specs['scipy'] sentinel = model.specs['sentinel'] - target_value = model.specs['target_value'] tsne = model.specs['tsne'] vectorize = model.specs['vectorize'] @@ -1282,10 +1292,6 @@ def create_features(model, X): logger.info("Original Features : %s", X.columns) logger.info("Feature Count : %d", X.shape[1]) - # Set classification flag - - classify = True if model_type == ModelType.classification else False - # Count zero and NaN values if counts_flag: @@ -1302,27 +1308,31 @@ def create_features(model, X): logger.info("Creating Base Features") all_features = np.zeros((X.shape[0], 1)) + model.feature_names = [] - for i, fc in enumerate(X): + for i, fname in enumerate(X): fnum = i + 1 - dtype = X[fc].dtypes - nunique = len(X[fc].unique()) + dtype = X[fname].dtypes + nunique = len(X[fname].unique()) # standard processing of numerical, categorical, and text features - if factors and fc in factors: - features = get_factors(model, X, fnum, fc, nunique, dtype, - encoder, rounding, sentinel) + if factors and fname in factors: + features, fnames = get_factors(model, X_train, X_test, y_train, fnum, fname, + nunique, dtype, encoder, rounding, sentinel) elif dtype == 'float64' or dtype == 'int64' or dtype == 'bool': - features = get_numerical_features(fnum, fc, X, nunique, dtype, - sentinel, logtransform, pvalue_level) + features, fnames = get_numerical_features(fnum, fname, X, nunique, dtype, + sentinel, logtransform, pvalue_level) elif dtype == 'object': - features = get_text_features(fnum, fc, X, nunique, vectorize, ngrams_max) + features, fnames = get_text_features(fnum, fname, X, nunique, vectorize, ngrams_max) else: raise TypeError("Base Feature Error with unrecognized type %s" % dtype) if features.shape[0] == all_features.shape[0]: + # add features all_features = np.column_stack((all_features, features)) + # add feature names + model.feature_names.extend(fnames) else: logger.info("Feature %s has the wrong number of rows: %d", - fc, features.shape[0]) + fname, features.shape[0]) all_features = np.delete(all_features, 0, axis=1) logger.info("New Feature Count : %d", all_features.shape[1]) @@ -1346,46 +1356,53 @@ def create_features(model, X): # Calculate the total, mean, standard deviation, and variance if numpy_flag: - np_features = create_numpy_features(base_features, sentinel) + np_features, fnames = create_numpy_features(base_features, sentinel) all_features = np.column_stack((all_features, np_features)) + model.feature_names.extend(fnames) logger.info("New Feature Count : %d", all_features.shape[1]) # Generate scipy features if scipy_flag: - sp_features = create_scipy_features(base_features, sentinel) + sp_features, fnames = create_scipy_features(base_features, sentinel) all_features = np.column_stack((all_features, sp_features)) + model.feature_names.extend(fnames) logger.info("New Feature Count : %d", all_features.shape[1]) # Create clustering features if clustering: - cfeatures = create_clusters(base_features, model) + cfeatures, fnames = create_clusters(base_features, model) all_features = np.column_stack((all_features, cfeatures)) + model.feature_names.extend(fnames) logger.info("New Feature Count : %d", all_features.shape[1]) # Create PCA features if pca: - pfeatures = create_pca_features(base_features, model) + pfeatures, fnames = create_pca_features(base_features, model) all_features = np.column_stack((all_features, pfeatures)) + model.feature_names.extend(fnames) logger.info("New Feature Count : %d", all_features.shape[1]) # Create Isomap features if isomap: - ifeatures = create_isomap_features(base_features, model) + ifeatures, fnames = create_isomap_features(base_features, model) all_features = np.column_stack((all_features, ifeatures)) + model.feature_names.extend(fnames) logger.info("New Feature Count : %d", all_features.shape[1]) # Create T-SNE features if tsne: - tfeatures = create_tsne_features(base_features, model) + tfeatures, fnames = create_tsne_features(base_features, model) all_features = np.column_stack((all_features, tfeatures)) + model.feature_names.extend(fnames) logger.info("New Feature Count : %d", all_features.shape[1]) # Return all transformed training and test features + assert all_features.shape[1] == len(model.feature_names), "Mismatched Features and Names" return all_features @@ -1456,6 +1473,11 @@ def select_features(model): model.X_train = X_train_new model.X_test = X_test_new + # Mask the feature names and test that feature and name lengths are equal + + model.feature_names = list(itertools.compress(model.feature_names, support)) + assert X_train_new.shape[1] == len(model.feature_names), "Mismatched Features and Names" + # Return the modified model return model @@ -1532,11 +1554,8 @@ def create_interactions(model, X): interactions = model.specs['interactions'] isample_pct = model.specs['isample_pct'] model_type = model.specs['model_type'] - n_jobs = model.specs['n_jobs'] poly_degree = model.specs['poly_degree'] predict_mode = model.specs['predict_mode'] - seed = model.specs['seed'] - verbosity = model.specs['verbosity'] # Extract model data @@ -1567,7 +1586,8 @@ def create_interactions(model, X): model.feature_map['poly_support'] = support else: support = model.feature_map['poly_support'] - pfeatures = get_polynomials(X[:, support], poly_degree) + pfeatures, pnames = get_polynomials(X[:, support], poly_degree) + model.feature_names.extend(pnames) logger.info("Polynomial Feature Count : %d", pfeatures.shape[1]) pfeatures = StandardScaler().fit_transform(pfeatures) all_features = np.hstack((all_features, pfeatures)) @@ -1576,6 +1596,7 @@ def create_interactions(model, X): logger.info("Skipping Interactions") # Return all features + assert all_features.shape[1] == len(model.feature_names), "Mismatched Features and Names" return all_features @@ -1660,9 +1681,11 @@ def remove_lv_features(model, X): else: support = model.feature_map['lv_support'] X_reduced = X[:, support] + model.feature_names = list(itertools.compress(model.feature_names, support)) logger.info("Reduced Feature Count : %d", X_reduced.shape[1]) else: X_reduced = X logger.info("Skipping Low-Variance Features") + assert X_reduced.shape[1] == len(model.feature_names), "Mismatched Features and Names" return X_reduced From 3eb7c259bbecd01f730626d3bebae229f0e9bc65 Mon Sep 17 00:00:00 2001 From: Mark Conway Date: Sun, 9 Feb 2020 15:54:06 -0500 Subject: [PATCH 064/129] update copyright and implement warnings update copyright and implement warnings --- alphapy/sport_flow.py | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/alphapy/sport_flow.py b/alphapy/sport_flow.py index 7ad59f3..1664b40 100644 --- a/alphapy/sport_flow.py +++ b/alphapy/sport_flow.py @@ -4,7 +4,7 @@ # Module : sport_flow # Created : July 11, 2013 # -# Copyright 2017 ScottFree Analytics LLC +# Copyright 2020 ScottFree Analytics LLC # Mark Conway & Robert D. Scott II # # Licensed under the Apache License, Version 2.0 (the "License"); @@ -50,8 +50,6 @@ import pandas as pd import sys import warnings -warnings.simplefilter(action='ignore', category=DeprecationWarning) -warnings.simplefilter(action='ignore', category=FutureWarning) import yaml @@ -636,6 +634,11 @@ def main(args=None): """ + # Suppress Warnings + + warnings.simplefilter(action='ignore', category=DeprecationWarning) + warnings.simplefilter(action='ignore', category=FutureWarning) + # Logging logging.basicConfig(format="[%(asctime)s] %(levelname)s\t%(message)s", From 70a9b2fe5c468f75d662c0cbd20b9302517b46e1 Mon Sep 17 00:00:00 2001 From: Mark Conway Date: Sun, 9 Feb 2020 15:55:20 -0500 Subject: [PATCH 065/129] plot feature names in importance plot plot feature names in importance plot --- alphapy/plots.py | 60 ++++++++++++++++++++++++++---------------------- 1 file changed, 33 insertions(+), 27 deletions(-) diff --git a/alphapy/plots.py b/alphapy/plots.py index 2fc2ffd..8c6bb99 100644 --- a/alphapy/plots.py +++ b/alphapy/plots.py @@ -4,7 +4,7 @@ # Module : plots # Created : July 11, 2013 # -# Copyright 2019 ScottFree Analytics LLC +# Copyright 2020 ScottFree Analytics LLC # Mark Conway & Robert D. Scott II # # Licensed under the Apache License, Version 2.0 (the "License"); @@ -63,8 +63,7 @@ from alphapy.utilities import remove_list_items from bokeh.plotting import figure, show, output_file -from itertools import cycle -from itertools import product +import itertools import logging import math import matplotlib @@ -270,6 +269,7 @@ def write_plot(vizlib, plot, plot_type, tag, directory=None): file_all = SSEP.join([directory, file_only]) logger.info("Writing plot to %s", file_all) if vizlib == 'matplotlib': + plot.tight_layout() plot.savefig(file_all) elif vizlib == 'seaborn': plot.savefig(file_all) @@ -389,36 +389,44 @@ def plot_importance(model, partition): plot_dir = get_plot_directory(model) pstring = datasets[partition] - # Get X, Y for correct partition - - X, y = get_partition_data(model, partition) - # For each algorithm that has importances, generate the plot. - n_top = 10 + n_top = 20 + for algo in model.algolist: logger.info("Feature Importances for Algorithm: %s", algo) try: - importances = model.importances[algo] - # forest was input parameter + # get feature importances + importances = np.array(model.importances[algo]) + imp_flag = True + except: + imp_flag = False + if imp_flag: + # sort the importances by index indices = np.argsort(importances)[::-1] + # get feature names + feature_names = np.array(model.fnames_algo[algo]) + n_features = len(feature_names) # log the feature ranking logger.info("Feature Ranking:") - for f in range(n_top): - logger.info("%d. Feature %d (%f)" % (f + 1, indices[f], importances[indices[f]])) + n_min = min(n_top, n_features) + for i in range(n_min): + logger.info("%d. %s (%f)" % (i + 1, + feature_names[indices[i]], + importances[indices[i]])) # plot the feature importances title = BSEP.join([algo, "Feature Importances [", pstring, "]"]) - plt.style.use('classic') plt.figure() plt.title(title) - plt.bar(list(range(n_top)), importances[indices][:n_top], color="b", align="center") - plt.xticks(list(range(n_top)), indices[:n_top]) - plt.xlim([-1, n_top]) + plt.barh(range(n_min), importances[indices][:n_min][::-1]) + plt.yticks(range(n_min), feature_names[indices][:n_min][::-1]) + plt.ylim([-1, n_min]) + plt.xlabel('Relative Importance') # save the plot tag = USEP.join([pstring, algo]) write_plot('matplotlib', plt, 'feature_importance', tag, plot_dir) - except: - logger.info("%s does not have feature importances", algo) + else: + logger.info("No Feature Importances for %s" % algo) # @@ -556,15 +564,12 @@ def plot_roc_curve(model, partition): plt.style.use('classic') plt.figure() - colors = cycle(['cyan', 'indigo', 'seagreen', 'yellow', 'blue', 'darkorange']) lw = 2 # Plot a ROC Curve for each algorithm. for algo in model.algolist: logger.info("ROC Curve for Algorithm: %s", algo) - # get estimator - estimator = model.estimators[algo] # compute ROC curve and ROC area for each class probas = model.probas[(algo, partition)] fpr, tpr, _ = roc_curve(y, probas) @@ -643,13 +648,13 @@ def plot_confusion_matrix(model, partition): logger.info('%s', cm) # normalize confusion matrix - cm = cm.astype('float') / cm.sum(axis=1)[:, np.newaxis] + cm_pct = cm.astype('float') / cm.sum(axis=1)[:, np.newaxis] # initialize plot _, ax = plt.subplots() # set the title of the confusion matrix - title = BSEP.join([algo, "Confusion Matrix [", pstring, "]"]) + title = algo + " Confusion Matrix: " + pstring + " [" + str(np.sum(cm)) + "]" plt.title(title) # only use the labels that appear in the data @@ -668,15 +673,16 @@ def plot_confusion_matrix(model, partition): rotation_mode="anchor") # loop over data dimensions and create text annotations - thresh = (cm.max() + cm.min()) / 2.0 + thresh = (cm_pct.max() + cm_pct.min()) / 2.0 for i in range(cm.shape[0]): for j in range(cm.shape[1]): - ax.text(j, i, format(cm[i, j], fmt), + cm_text = format(cm_pct[i, j], fmt) + " [" + str(cm[i, j]) + "]" + ax.text(j, i, cm_text, ha="center", va="center", - color="white" if cm[i, j] > thresh else "black") + color="white" if cm_pct[i, j] >= thresh else "black") # show the color bar - im = ax.imshow(cm, interpolation='nearest', cmap=cmap) + im = ax.imshow(cm_pct, interpolation='nearest', cmap=cmap) ax.figure.colorbar(im, ax=ax) # save the chart From 9092b52252cb4dc3374ed7bb387e29be6f49e4a0 Mon Sep 17 00:00:00 2001 From: Mark Conway Date: Sun, 9 Feb 2020 15:56:46 -0500 Subject: [PATCH 066/129] feature names and refactor for pandas 1.0 feature names and refactor for pandas 1.0 --- alphapy/model.py | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/alphapy/model.py b/alphapy/model.py index a7865dc..6123438 100644 --- a/alphapy/model.py +++ b/alphapy/model.py @@ -4,7 +4,7 @@ # Module : model # Created : July 11, 2013 # -# Copyright 2019 ScottFree Analytics LLC +# Copyright 2020 ScottFree Analytics LLC # Mark Conway & Robert D. Scott II # # Licensed under the Apache License, Version 2.0 (the "License"); @@ -43,6 +43,7 @@ from copy import copy from datetime import datetime +import itertools import joblib from keras.models import load_model import logging @@ -161,6 +162,8 @@ def __init__(self, except: raise KeyError("Model specs must include the key: algorithms") self.best_algo = None + # feature names + self.feature_names = [] # feature map self.feature_map = {} # Key: (algorithm) @@ -168,6 +171,7 @@ def __init__(self, self.importances = {} self.coefs = {} self.support = {} + self.fnames_algo = {} # Keys: (algorithm, partition) self.preds = {} self.probas = {} @@ -1202,7 +1206,7 @@ def save_predictions(model, tag, partition): if found_pdate: pd_indices = pf[pf.date >= predict_date].index.tolist() - pf = pf.ix[pd_indices] + pf = pf.iloc[pd_indices] else: pd_indices = pf.index.tolist() From 08ebf538a3de555d0e1a7a1018a907e98f4eb876 Mon Sep 17 00:00:00 2001 From: Mark Conway Date: Sun, 9 Feb 2020 15:57:53 -0500 Subject: [PATCH 067/129] feature names at algorithm level based on RFE feature names at algorithm level based on RFE --- alphapy/optimize.py | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/alphapy/optimize.py b/alphapy/optimize.py index 39d316a..61f379f 100644 --- a/alphapy/optimize.py +++ b/alphapy/optimize.py @@ -4,7 +4,7 @@ # Module : optimize # Created : July 11, 2013 # -# Copyright 2017 ScottFree Analytics LLC +# Copyright 2020 ScottFree Analytics LLC # Mark Conway & Robert D. Scott II # # Licensed under the Apache License, Version 2.0 (the "License"); @@ -29,9 +29,9 @@ from alphapy.globals import ModelType from datetime import datetime +import itertools import logging import numpy as np -from sklearn.feature_selection import RFE from sklearn.feature_selection import RFECV from sklearn.feature_selection import SelectPercentile from sklearn.model_selection import GridSearchCV @@ -68,10 +68,6 @@ def rfecv_search(model, algo): The model object with the RFE support vector and the best estimator. - See Also - -------- - rfe_search - Notes ----- If a scoring function is available, then AlphaPy can perform RFE @@ -113,10 +109,14 @@ def rfecv_search(model, algo): logger.info("Algorithm: %s, Selected Features: %d, Ranking: %s", algo, selector.n_features_, selector.ranking_) - # Record the new estimator and support vector + # Record the new estimator, support vector, feature names, and importances - model.estimators[algo] = selector.estimator_ + best_estimator = selector.estimator_ + model.estimators[algo] = best_estimator model.support[algo] = selector.support_ + model.fnames_algo[algo] = list(itertools.compress(model.fnames_algo[algo], selector.support_)) + if hasattr(best_estimator, "feature_importances_"): + model.importances[algo] = best_estimator.feature_importances_ # Return the model with the support vector From d9efcb04eef2a43f0359640fe73597b64633ff96 Mon Sep 17 00:00:00 2001 From: Mark Conway Date: Sun, 9 Feb 2020 15:59:22 -0500 Subject: [PATCH 068/129] market_flow cleanup market_flow cleanup --- alphapy/market_flow.py | 21 ++++++++++++--------- 1 file changed, 12 insertions(+), 9 deletions(-) diff --git a/alphapy/market_flow.py b/alphapy/market_flow.py index 01a6d7c..37627d9 100644 --- a/alphapy/market_flow.py +++ b/alphapy/market_flow.py @@ -4,7 +4,7 @@ # Module : market_flow # Created : July 11, 2013 # -# Copyright 2019 ScottFree Analytics LLC +# Copyright 2020 ScottFree Analytics LLC # Mark Conway & Robert D. Scott II # # Licensed under the Apache License, Version 2.0 (the "License"); @@ -50,8 +50,6 @@ import pandas as pd import sys import warnings -warnings.simplefilter(action='ignore', category=DeprecationWarning) -warnings.simplefilter(action='ignore', category=FutureWarning) import yaml @@ -97,7 +95,7 @@ def get_market_config(): specs['create_model'] = cfg['market']['create_model'] fractal = cfg['market']['data_fractal'] try: - test_interval = pd.to_timedelta(fractal) + _ = pd.to_timedelta(fractal) except: logger.info("data_fractal [%s] is an invalid pandas offset", fractal) @@ -285,13 +283,15 @@ def market_pipeline(model, market_specs): # Run an analysis to create the model if create_model: + logger.info("Creating Model") # apply features to all of the frames vmapply(group, features, functions) vmapply(group, [target], functions) # run the analysis, including the model pipeline a = Analysis(model, group) - results = run_analysis(a, lag_period, forecast_period, - leaders, predict_history) + run_analysis(a, lag_period, forecast_period, leaders, predict_history) + else: + logger.info("No Model (System Only)") # Run a system @@ -346,6 +346,11 @@ def main(args=None): """ + # Suppress Warnings + + warnings.simplefilter(action='ignore', category=DeprecationWarning) + warnings.simplefilter(action='ignore', category=FutureWarning) + # Logging logging.basicConfig(format="[%(asctime)s] %(levelname)s\t%(message)s", @@ -418,9 +423,7 @@ def main(args=None): logger.info("Creating directory %s", output_dir) os.makedirs(output_dir) - # Create a model from the arguments - - logger.info("Creating Model") + # Create a model object from the specifications model = Model(model_specs) # Start the pipeline From d56d55282f43b8f628bbf3680cec7d24647661ad Mon Sep 17 00:00:00 2001 From: Mark Conway Date: Sun, 9 Feb 2020 16:00:32 -0500 Subject: [PATCH 069/129] rename all_features to X_all rename all_features to X_all --- alphapy/__main__.py | 67 +++++++++++++++++++++++++-------------------- 1 file changed, 37 insertions(+), 30 deletions(-) diff --git a/alphapy/__main__.py b/alphapy/__main__.py index 3293902..0e4324b 100644 --- a/alphapy/__main__.py +++ b/alphapy/__main__.py @@ -4,7 +4,7 @@ # Module : __main__ # Created : July 11, 2013 # -# Copyright 2017 ScottFree Analytics LLC +# Copyright 2020 ScottFree Analytics LLC # Mark Conway & Robert D. Scott II # # Licensed under the Apache License, Version 2.0 (the "License"); @@ -69,8 +69,6 @@ from sklearn.model_selection import train_test_split import sys import warnings -warnings.simplefilter(action='ignore', category=DeprecationWarning) -warnings.simplefilter(action='ignore', category=FutureWarning) # @@ -164,30 +162,30 @@ def training_pipeline(model): if X_train.shape[1] == X_test.shape[1]: split_point = X_train.shape[0] - X = pd.concat([X_train, X_test]) + X_all = pd.concat([X_train, X_test]) else: raise IndexError("The number of training and test columns [%d, %d] must match." % (X_train.shape[1], X_test.shape[1])) # Apply treatments to the feature matrix - all_features = apply_treatments(model, X) + X_all = apply_treatments(model, X_all) # Drop features - all_features = drop_features(all_features, drop) + X_all = drop_features(X_all, drop) # Save the train and test files with extracted and dropped features datestamp = get_datestamp() data_dir = SSEP.join([directory, 'input']) - df_train = all_features.iloc[:split_point, :] - df_train = pd.concat([df_train, pd.DataFrame(y_train, columns=[target])], axis=1) + df_train = X_all.iloc[:split_point, :] + df_train[target] = y_train output_file = USEP.join([model.train_file, datestamp]) - write_frame(df_train, data_dir, output_file, extension, separator) - df_test = all_features.iloc[split_point:, :] + write_frame(df_train, data_dir, output_file, extension, separator, index=False) + df_test = X_all.iloc[split_point:, :] if y_test.any(): - df_test = pd.concat([df_test, pd.DataFrame(y_test, columns=[target])], axis=1) + df_test[target] = y_test output_file = USEP.join([model.test_file, datestamp]) - write_frame(df_test, data_dir, output_file, extension, separator) + write_frame(df_test, data_dir, output_file, extension, separator, index=False) # Create crosstabs for any categorical features @@ -196,20 +194,20 @@ def training_pipeline(model): # Create initial features - all_features = create_features(model, all_features) - X_train, X_test = np.array_split(all_features, [split_point]) + X_all = create_features(model, X_all, X_train, X_test, y_train) + X_train, X_test = np.array_split(X_all, [split_point]) model = save_features(model, X_train, X_test) # Generate interactions - all_features = create_interactions(model, all_features) - X_train, X_test = np.array_split(all_features, [split_point]) + X_all = create_interactions(model, X_all) + X_train, X_test = np.array_split(X_all, [split_point]) model = save_features(model, X_train, X_test) # Remove low-variance features - all_features = remove_lv_features(model, all_features) - X_train, X_test = np.array_split(all_features, [split_point]) + X_all = remove_lv_features(model, X_all) + X_train, X_test = np.array_split(X_all, [split_point]) model = save_features(model, X_train, X_test) # Shuffle the data [if specified] @@ -252,6 +250,8 @@ def training_pipeline(model): logger.info("Algorithm %s not found", algo) # initial fit model = first_fit(model, algo, est) + # copy feature name master into feature names per algorithm + model.fnames_algo[algo] = model.feature_names # recursive feature elimination if rfe: has_coef = hasattr(est, "coef_") @@ -325,7 +325,9 @@ def prediction_pipeline(model): model_type = model.specs['model_type'] rfe = model.specs['rfe'] - # Get all data. We need original train and test for interactions. + # Get all data. We need original train and test for encodings. + + X_train, y_train = get_data(model, Partition.train) partition = Partition.predict X_predict, _ = get_data(model, partition) @@ -340,19 +342,19 @@ def prediction_pipeline(model): logger.info("Number of Prediction Columns : %d", X_predict.shape[1]) # Apply treatments to the feature matrix - all_features = apply_treatments(model, X_predict) + X_all = apply_treatments(model, X_predict) # Drop features - all_features = drop_features(all_features, drop) + X_all = drop_features(X_all, drop) # Create initial features - all_features = create_features(model, all_features) + X_all = create_features(model, X_all, X_train, X_predict, y_train) # Generate interactions - all_features = create_interactions(model, all_features) + X_all = create_interactions(model, X_all) # Remove low-variance features - all_features = remove_lv_features(model, all_features) + X_all = remove_lv_features(model, X_all) # Load the univariate support vector, if any @@ -360,8 +362,8 @@ def prediction_pipeline(model): logger.info("Getting Univariate Support") try: support = model.feature_map['uni_support'] - all_features = all_features[:, support] - logger.info("New Feature Count : %d", all_features.shape[1]) + X_all = X_all[:, support] + logger.info("New Feature Count : %d", X_all.shape[1]) except: logger.info("No Univariate Support") @@ -371,8 +373,8 @@ def prediction_pipeline(model): logger.info("Getting RFE Support") try: support = model.feature_map['rfe_support'] - all_features = all_features[:, support] - logger.info("New Feature Count : %d", all_features.shape[1]) + X_all = X_all[:, support] + logger.info("New Feature Count : %d", X_all.shape[1]) except: logger.info("No RFE Support") @@ -383,9 +385,9 @@ def prediction_pipeline(model): logger.info("Making Predictions") tag = 'BEST' - model.preds[(tag, partition)] = predictor.predict(all_features) + model.preds[(tag, partition)] = predictor.predict(X_all) if model_type == ModelType.classification: - model.probas[(tag, partition)] = predictor.predict_proba(all_features)[:, 1] + model.probas[(tag, partition)] = predictor.predict_proba(X_all)[:, 1] # Save predictions save_predictions(model, tag, partition) @@ -444,6 +446,11 @@ def main(args=None): """ + # Suppress Warnings + + warnings.simplefilter(action='ignore', category=DeprecationWarning) + warnings.simplefilter(action='ignore', category=FutureWarning) + # Logging logging.basicConfig(format="[%(asctime)s] %(levelname)s\t%(message)s", From 121274129b432c183b8da1759e8dcd45791feb5f Mon Sep 17 00:00:00 2001 From: Mark Conway Date: Sun, 9 Feb 2020 16:01:33 -0500 Subject: [PATCH 070/129] Version 2.4.0: Feature Names and New Encoders Version 2.4.0: Feature Names and New Encoders --- docs/conf.py | 6 +++--- setup.py | 6 +++--- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/docs/conf.py b/docs/conf.py index 0a0a382..4bf23a0 100644 --- a/docs/conf.py +++ b/docs/conf.py @@ -54,7 +54,7 @@ # General information about the project. project = 'AlphaPy' -copyright = '2019, ScottFree Analytics LLC' +copyright = '2020, ScottFree Analytics LLC' author = 'Robert D. Scott II, Mark Conway' # The version info for the project you're documenting, acts as replacement for @@ -62,9 +62,9 @@ # built documents. # # The short X.Y version. -version = '2.3.9' +version = '2.4.0' # The full version, including alpha/beta/rc tags. -release = '2.3.9' +release = '2.4.0' # The language for content autogenerated by Sphinx. Refer to documentation # for a list of supported languages. diff --git a/setup.py b/setup.py index be3652f..557da67 100644 --- a/setup.py +++ b/setup.py @@ -10,7 +10,7 @@ MAINTAINER_EMAIL = 'scottfree.analytics@scottfreellc.com' URL = "https://github.com/ScottFreeLLC/AlphaPy" LICENSE = "Apache License, Version 2" -VERSION = "2.3.9" +VERSION = "2.4.0" classifiers = ['Development Status :: 4 - Beta', 'Programming Language :: Python', @@ -33,11 +33,11 @@ 'keras>=2.2', 'matplotlib>=3.0', 'numpy>=1.17', - 'pandas>=0.24', + 'pandas>=1.0', 'pandas-datareader>=0.8', 'pyfolio>=0.9', 'pyyaml>=5.0', - 'scikit-learn>=0.21', + 'scikit-learn>=0.22', 'scipy>=1.1', 'seaborn>=0.9', 'tensorflow>=1.15', From b5eb4dded3b52d72aaa41328fdf40e9257a2e3f3 Mon Sep 17 00:00:00 2001 From: Mark Conway Date: Sun, 1 Mar 2020 12:49:42 -0500 Subject: [PATCH 071/129] replace factorize with target encoding in examples replace factorize with target encoding in examples --- alphapy/examples/Kaggle/config/model.yml | 2 +- alphapy/examples/NCAAB/config/model.yml | 2 +- alphapy/examples/Trading Model/config/model.yml | 2 +- alphapy/examples/Trading System/config/model.yml | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/alphapy/examples/Kaggle/config/model.yml b/alphapy/examples/Kaggle/config/model.yml index 3fd559f..ac84366 100644 --- a/alphapy/examples/Kaggle/config/model.yml +++ b/alphapy/examples/Kaggle/config/model.yml @@ -54,7 +54,7 @@ features: option : True encoding : rounding : 2 - type : factorize + type : target factors : [] interactions : option : True diff --git a/alphapy/examples/NCAAB/config/model.yml b/alphapy/examples/NCAAB/config/model.yml index a541cc6..7ee58ab 100644 --- a/alphapy/examples/NCAAB/config/model.yml +++ b/alphapy/examples/NCAAB/config/model.yml @@ -56,7 +56,7 @@ features: option : False encoding : rounding : 3 - type : factorize + type : target factors : ['line', 'delta.wins', 'delta.losses', 'delta.ties', 'delta.point_win_streak', 'delta.point_loss_streak', 'delta.cover_win_streak', 'delta.cover_loss_streak', diff --git a/alphapy/examples/Trading Model/config/model.yml b/alphapy/examples/Trading Model/config/model.yml index 82d4574..61f20f2 100644 --- a/alphapy/examples/Trading Model/config/model.yml +++ b/alphapy/examples/Trading Model/config/model.yml @@ -57,7 +57,7 @@ features: option : False encoding : rounding : 3 - type : factorize + type : target factors : [] interactions : option : True diff --git a/alphapy/examples/Trading System/config/model.yml b/alphapy/examples/Trading System/config/model.yml index 292f656..615e6a1 100644 --- a/alphapy/examples/Trading System/config/model.yml +++ b/alphapy/examples/Trading System/config/model.yml @@ -54,7 +54,7 @@ features: option : False encoding : rounding : 3 - type : factorize + type : target factors : [] interactions : option : True From 095a7a10d586eb8b8e02355d7828f6262cc613cf Mon Sep 17 00:00:00 2001 From: Mark Conway Date: Mon, 2 Mar 2020 20:30:37 -0500 Subject: [PATCH 072/129] pandas .ix to .loc and from_items to from_dict pandas .ix to .loc and from_items to from_dict --- alphapy/portfolio.py | 22 +++++++++++----------- alphapy/system.py | 4 ++-- 2 files changed, 13 insertions(+), 13 deletions(-) diff --git a/alphapy/portfolio.py b/alphapy/portfolio.py index 767f245..8343222 100644 --- a/alphapy/portfolio.py +++ b/alphapy/portfolio.py @@ -4,7 +4,7 @@ # Module : portfolio # Created : July 11, 2013 # -# Copyright 2017 ScottFree Analytics LLC +# Copyright 2020 ScottFree Analytics LLC # Mark Conway & Robert D. Scott II # # Licensed under the Apache License, Version 2.0 (the "License"); @@ -437,7 +437,7 @@ def valuate_position(position, tdate): # get current price pdata = position.pdata if tdate in pdata.index: - cp = float(pdata.ix[tdate]['close']) + cp = float(pdata.loc[tdate]['close']) # start valuation multiplier = position.multiplier netpos = 0 @@ -524,7 +524,7 @@ def close_position(p, position, tdate): tradesize = -pq position.date = tdate pdata = position.pdata - cp = pdata.ix[tdate]['close'] + cp = pdata.loc[tdate]['close'] newtrade = Trade(position.name, tradesize, cp, tdate) p = update_portfolio(p, position, newtrade) position.quantity = 0 @@ -719,7 +719,7 @@ def balance(p, tdate, cashlevel): estr = '.'.join('pos', weightby) bdata[i] = eval(estr) else: - bdata[i] = pos.pdata.ix[tdate][weightby] + bdata[i] = pos.pdata.loc[tdate][weightby] if invert: bweights = (2 * bdata.mean() - bdata) / sum(bdata) else: @@ -728,7 +728,7 @@ def balance(p, tdate, cashlevel): for i, pos in enumerate(positions): multiplier = pos.multiplier bdelta = bweights[i] * pvalue - pos.value - cp = pos.pdata.ix[tdate]['close'] + cp = pos.pdata.loc[tdate]['close'] tradesize = math.trunc(bdelta / cp) ntv = abs(tradesize) * cp * multiplier if tradesize > 0: @@ -792,7 +792,7 @@ def kick_out(p, tdate): estr = '.'.join('pos', koby) kovalue[i] = eval(estr) else: - kovalue[i] = pos.pdata.ix[tdate][koby] + kovalue[i] = pos.pdata.loc[tdate][koby] koorder = np.argsort(np.argsort(kovalues)) if descending: koorder = [i for i in reversed(koorder)] @@ -985,7 +985,7 @@ def exec_trade(p, name, order, quantity, price, tdate): else: if order == Orders.le or order == Orders.se: pf = Frame.frames[frame_name(name, p.space)].df - cv = float(pf.ix[tdate][p.posby]) + cv = float(pf.loc[tdate][p.posby]) tsize = math.trunc((p.value * p.fixedfrac) / cv) if quantity < 0: tsize = -tsize @@ -1101,7 +1101,7 @@ def gen_portfolio(model, system, group, tframe, for d in drange: # process today's trades if d in trange: - trades = tframe.ix[d] + trades = tframe.loc[d] if isinstance(trades, Series): trades = DataFrame(trades).transpose() for t in trades.iterrows(): @@ -1114,7 +1114,7 @@ def gen_portfolio(model, system, group, tframe, logger.info("Trade could not be executed for %s", row['name']) # iterate through current positions positions = p.positions - pfrow = pf.ix[d] + pfrow = pf.loc[d] for key in positions: pos = positions[key] if pos.quantity > 0: @@ -1135,7 +1135,7 @@ def gen_portfolio(model, system, group, tframe, logger.info("Recording Returns Frame") rspace = Space(system, 'returns', gspace.fractal) - rf = DataFrame.from_items(rs, orient='index', columns=['return']) + rf = DataFrame.from_dict(dict(rs), orient='index', columns=['return']) rfname = frame_name(gname, rspace) write_frame(rf, system_dir, rfname, extension, separator, index=True, index_label='date') @@ -1154,7 +1154,7 @@ def gen_portfolio(model, system, group, tframe, logger.info("Recording Transactions Frame") tspace = Space(system, 'transactions', gspace.fractal) - tf = DataFrame.from_items(ts, orient='index', columns=['amount', 'price', 'symbol']) + tf = DataFrame.from_dict(dict(ts), orient='index', columns=['amount', 'price', 'symbol']) tfname = frame_name(gname, tspace) write_frame(tf, system_dir, tfname, extension, separator, index=True, index_label='date') diff --git a/alphapy/system.py b/alphapy/system.py index 7c1a859..6930ab9 100644 --- a/alphapy/system.py +++ b/alphapy/system.py @@ -4,7 +4,7 @@ # Module : system # Created : July 11, 2013 # -# Copyright 2017 ScottFree Analytics LLC +# Copyright 2020 ScottFree Analytics LLC # Mark Conway & Robert D. Scott II # # Licensed under the Apache License, Version 2.0 (the "License"); @@ -367,7 +367,7 @@ def run_system(model, if gtlist: tspace = Space(system_name, "trades", group.space.fractal) gtlist = sorted(gtlist, key=lambda x: x[0]) - tf = DataFrame.from_items(gtlist, orient='index', columns=Trade.states) + tf = DataFrame.from_dict(dict(gtlist), orient='index', columns=Trade.states) tfname = frame_name(gname, tspace) system_dir = SSEP.join([directory, 'systems']) labels = ['date'] From f99cd4ae24babd9606008138144416806c98313a Mon Sep 17 00:00:00 2001 From: Mark Conway Date: Mon, 2 Mar 2020 20:54:46 -0500 Subject: [PATCH 073/129] Version 2.4.1: replace pandas .ix and DataFrame.from_items migrate to .loc and DataFrame.from_dict --- docs/conf.py | 4 ++-- setup.py | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/docs/conf.py b/docs/conf.py index 4bf23a0..a8783f0 100644 --- a/docs/conf.py +++ b/docs/conf.py @@ -62,9 +62,9 @@ # built documents. # # The short X.Y version. -version = '2.4.0' +version = '2.4.1' # The full version, including alpha/beta/rc tags. -release = '2.4.0' +release = '2.4.1' # The language for content autogenerated by Sphinx. Refer to documentation # for a list of supported languages. diff --git a/setup.py b/setup.py index 557da67..fba1555 100644 --- a/setup.py +++ b/setup.py @@ -10,7 +10,7 @@ MAINTAINER_EMAIL = 'scottfree.analytics@scottfreellc.com' URL = "https://github.com/ScottFreeLLC/AlphaPy" LICENSE = "Apache License, Version 2" -VERSION = "2.4.0" +VERSION = "2.4.1" classifiers = ['Development Status :: 4 - Beta', 'Programming Language :: Python', From ee8f46fac3388005b69102a9c3628b97d4bbffd7 Mon Sep 17 00:00:00 2001 From: Mark Conway Date: Sun, 15 Mar 2020 09:31:15 -0400 Subject: [PATCH 074/129] Create calendrical.py Calendrical Calculations --- alphapy/calendrical.py | 1275 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 1275 insertions(+) create mode 100644 alphapy/calendrical.py diff --git a/alphapy/calendrical.py b/alphapy/calendrical.py new file mode 100644 index 0000000..c368536 --- /dev/null +++ b/alphapy/calendrical.py @@ -0,0 +1,1275 @@ +################################################################################ +# +# Package : calendrical +# Created : July 11, 2017 +# Reference : Calendrical Calculations, Cambridge Press, 2002 +# +# Copyright 2020 ScottFree Analytics LLC +# Mark Conway & Robert D. Scott II +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# +################################################################################ + + +# +# Imports +# + +import calendar +import logging +import math +import pandas as pd + + +# +# Initialize logger +# + +logger = logging.getLogger(__name__) + + +# +# Function expand_dates +# + +def expand_dates(date_list): + expanded_dates = [] + for item in date_list: + if type(item) == str: + expanded_dates.append(item) + elif type(item) == list: + start_date = item[0] + end_date = item[1] + dates_dt = pd.date_range(start_date, end_date).tolist() + dates_str = [x.strftime('%Y-%m-%d') for x in dates_dt] + expanded_dates.extend(dates_str) + else: + logger.info("Error in date: %s" % item) + return expanded_dates + + +# +# Function biz_day_month +# + +def biz_day_month(rdate): + r"""Calculate the business day of the month. + + Parameters + ---------- + rdate : int + RDate date format. + + Returns + ------- + bdm : int + Business day of month. + """ + + gyear, gmonth, _ = rdate_to_gdate(rdate) + rdate1 = gdate_to_rdate(gyear, gmonth, 1) + + bdm = 0 + index_date = rdate1 + while index_date <= rdate: + dw = day_of_week(index_date) + week_day = dw >= 1 and dw <= 5 + if week_day: + bdm += 1 + index_date += 1 + + holidays = set_holidays(gyear, True) + for h in holidays: + holiday = holidays[h] + in_period = holiday >= rdate1 and holiday <= rdate + dwh = day_of_week(holiday) + week_day = dwh >= 1 and dwh <= 5 + if in_period and week_day: + bdm -= 1 + return bdm + + +# +# Function biz_day_week +# + +def biz_day_week(rdate): + r"""Calculate the business day of the week. + + Parameters + ---------- + rdate : int + RDate date format. + + Returns + ------- + bdw : int + Business day of week. + """ + + gyear, _, _ = rdate_to_gdate(rdate) + dw = day_of_week(rdate) + week_day = dw >= 1 and dw <= 5 + + bdw = 0 + if week_day: + rdate1 = rdate - dw + 1 + rdate2 = rdate - 1 + holidays = set_holidays(gyear, True) + for h in holidays: + holiday = holidays[h] + in_period = holiday >= rdate1 and holiday <= rdate2 + if in_period: + bdw -= 1 + return bdw + + +# +# Function day_of_week +# + +def day_of_week(rdate): + r"""Get the ordinal day of the week. + + Parameters + ---------- + rdate : int + RDate date format. + + Returns + ------- + dw : int + Ordinal day of the week. + """ + dw = rdate % 7 + return dw + + +# +# Function day_of_year +# + +def day_of_year(gyear, gmonth, gday): + r"""Calculate the day number of the given calendar year. + + Parameters + ---------- + gyear : int + Gregorian year. + gmonth : int + Gregorian month. + gday : int + Gregorian day. + + Returns + ------- + dy : int + Day number of year in RDate format. + """ + dy = subtract_dates(gyear - 1, 12, 31, gyear, gmonth, gday) + return dy + + +# +# Function days_left_in_year +# + +def days_left_in_year(gyear, gmonth, gday): + r"""Calculate the number of days remaining in the calendar year. + + Parameters + ---------- + gyear : int + Gregorian year. + gmonth : int + Gregorian month. + gday : int + Gregorian day. + + Returns + ------- + days_left : int + Calendar days remaining in RDate format. + """ + days_left = subtract_dates(gyear, gmonth, gday, gyear, 12, 31) + return days_left + + +# +# Function first_kday +# + +def first_kday(k, gyear, gmonth, gday): + r"""Calculate the first kday in RDate format. + + Parameters + ---------- + k : int + Day of the week. + gyear : int + Gregorian year. + gmonth : int + Gregorian month. + gday : int + Gregorian day. + + Returns + ------- + fkd : int + first-kday in RDate format. + """ + fkd = nth_kday(1, k, gyear, gmonth, gday) + return fkd + + +# +# Function gdate_to_rdate +# + +def gdate_to_rdate(gyear, gmonth, gday): + r"""Convert Gregorian date to RDate format. + + Parameters + ---------- + gyear : int + Gregorian year. + gmonth : int + Gregorian month. + gday : int + Gregorian day. + + Returns + ------- + rdate : int + RDate date format. + """ + + if gmonth <= 2: + rfactor = 0 + elif gmonth > 2 and leap_year(gyear): + rfactor = -1 + else: + rfactor = -2 + + rdate = 365 * (gyear - 1) \ + + math.floor((gyear - 1) / 4) \ + - math.floor((gyear - 1) / 100) \ + + math.floor((gyear - 1) / 400) \ + + math.floor(((367 * gmonth) - 362) / 12) \ + + gday + rfactor + return(rdate) + + +# +# Function get_nth_kday_of_month +# + +def get_nth_kday_of_month(gday, gmonth, gyear): + r"""Convert Gregorian date to RDate format. + + Parameters + ---------- + gday : int + Gregorian day. + gmonth : int + Gregorian month. + gyear : int + Gregorian year. + + Returns + ------- + nth : int + Ordinal number of a given day's occurrence within the month, + for example, the third Friday of the month. + """ + + this_month = calendar.monthcalendar(gyear, gmonth) + nth_kday_tuple = next(((i, e.index(gday)) for i, e in enumerate(this_month) if gday in e), None) + tuple_row = nth_kday_tuple[0] + tuple_pos = nth_kday_tuple[1] + nth = tuple_row + 1 + if tuple_row > 0 and this_month[0][tuple_pos] == 0: + nth -= 1 + return nth + + +# +# Function get_rdate +# + +def get_rdate(row): + r"""Extract RDate from a dataframe. + + Parameters + ---------- + row : pandas.DataFrame + Row of a dataframe containing year, month, and day. + + Returns + ------- + rdate : int + RDate date format. + """ + return gdate_to_rdate(row['year'], row['month'], row['day']) + + +# +# Function kday_after +# + +def kday_after(rdate, k): + r"""Calculate the day after a given RDate. + + Parameters + ---------- + rdate : int + RDate date format. + k : int + Day of the week. + + Returns + ------- + kda : int + kday-after in RDate format. + """ + kda = kday_on_before(rdate + 7, k) + return kda + + +# +# Function kday_before +# + +def kday_before(rdate, k): + r"""Calculate the day before a given RDate. + + Parameters + ---------- + rdate : int + RDate date format. + k : int + Day of the week. + + Returns + ------- + kdb : int + kday-before in RDate format. + """ + kdb = kday_on_before(rdate - 1, k) + return kdb + + +# +# Function kday_nearest +# + +def kday_nearest(rdate, k): + r"""Calculate the day nearest a given RDate. + + Parameters + ---------- + rdate : int + RDate date format. + k : int + Day of the week. + + Returns + ------- + kdn : int + kday-nearest in RDate format. + """ + kdn = kday_on_before(rdate + 3, k) + return kdn + + +# +# Function kday_on_after +# + +def kday_on_after(rdate, k): + r"""Calculate the day on or after a given RDate. + + Parameters + ---------- + rdate : int + RDate date format. + k : int + Day of the week. + + Returns + ------- + kdoa : int + kday-on-or-after in RDate format. + """ + kdoa = kday_on_before(rdate + 6, k) + return kdoa + + +# +# Function kday_on_before +# + +def kday_on_before(rdate, k): + r"""Calculate the day on or before a given RDate. + + Parameters + ---------- + rdate : int + RDate date format. + k : int + Day of the week. + + Returns + ------- + kdob : int + kday-on-or-before in RDate format. + """ + kdob = rdate - day_of_week(rdate - k) + return kdob + + +# +# Function last_kday +# + +def last_kday(k, gyear, gmonth, gday): + r"""Calculate the last kday in RDate format. + + Parameters + ---------- + k : int + Day of the week. + gyear : int + Gregorian year. + gmonth : int + Gregorian month. + gday : int + Gregorian day. + + Returns + ------- + lkd : int + last-kday in RDate format. + """ + lkd = nth_kday(-1, k, gyear, gmonth, gday) + return lkd + + +# +# Function leap_year +# + +def leap_year(gyear): + r"""Determine if this is a Gregorian leap year. + + Parameters + ---------- + gyear : int + Gregorian year. + + Returns + ------- + leap_year : bool + True if a Gregorian leap year, else False. + """ + + mod1 = (gyear % 4 == 0) + mod2 = True + if gyear % 100 == 0: + mod2 = gyear % 400 == 0 + + leap_year = False + if mod1 and mod2: + leap_year = True + return leap_year + + +# +# Function next_event +# + +def next_event(rdate, events): + r"""Find the next event after a given date. + + Parameters + ---------- + rdate : int + RDate date format. + events : list of RDate (int) + Monthly events in RDate format. + + Returns + ------- + event : RDate (int) + Next event in RDate format. + """ + try: + event = next(e for e in events if e > rdate) + except: + event = 0 + return event + + +# +# Function next_holiday +# + +def next_holiday(rdate, holidays): + r"""Find the next holiday after a given date. + + Parameters + ---------- + rdate : int + RDate date format. + holidays : dict of RDate (int) + Holidays in RDate format. + + Returns + ------- + holiday : RDate (int) + Next holiday in RDate format. + """ + try: + holiday = next(h for h in sorted(holidays.values()) if h > rdate) + except: + holiday = 0 + return holiday + + +# +# Function nth_bizday +# + +def nth_bizday(n, gyear, gmonth): + r"""Calculate the nth business day in a month. + + Parameters + ---------- + n : int + Number of the business day to get. + gyear : int + Gregorian year. + gmonth : int + Gregorian month. + + Returns + ------- + bizday : int + Nth business day of a given month in RDate format. + """ + + rdate = gdate_to_rdate(gyear, gmonth, 1) + holidays = set_holidays(gyear, True) + ibd = 0 + idate = rdate + while (ibd < n): + dw = day_of_week(idate) + week_day = dw >= 1 and dw <= 5 + if week_day and idate not in holidays.values(): + ibd += 1 + bizday = idate + idate += 1 + return bizday + + +# +# Function nth_kday +# + +def nth_kday(n, k, gyear, gmonth, gday): + r"""Calculate the nth-kday in RDate format. + + Parameters + ---------- + n : int + Occurrence of a given day counting in either direction. + k : int + Day of the week. + gyear : int + Gregorian year. + gmonth : int + Gregorian month. + gday : int + Gregorian day. + + Returns + ------- + nthkday : int + nth-kday in RDate format. + """ + + rdate = gdate_to_rdate(gyear, gmonth, gday) + if n > 0: + nthkday = 7 * n + kday_before(rdate, k) + else: + nthkday = 7 * n + kday_after(rdate, k) + return nthkday + + +# +# Function previous_event +# + +def previous_event(rdate, events): + r"""Find the previous event before a given date. + + Parameters + ---------- + rdate : int + RDate date format. + events : list of RDate (int) + Monthly events in RDate format. + + Returns + ------- + event : RDate (int) + Previous event in RDate format. + """ + try: + event = next(e for e in sorted(events, reverse=True) if e < rdate) + except: + event = 0 + return event + + +# +# Function previous_holiday +# + +def previous_holiday(rdate, holidays): + r"""Find the previous holiday before a given date. + + Parameters + ---------- + rdate : int + RDate date format. + holidays : dict of RDate (int) + Holidays in RDate format. + + Returns + ------- + holiday : RDate (int) + Previous holiday in RDate format. + """ + try: + holiday = next(h for h in sorted(holidays.values(), reverse=True) if h < rdate) + except: + holiday = 0 + return holiday + + +# +# Function rdate_to_gdate +# + +def rdate_to_gdate(rdate): + r"""Convert RDate format to Gregorian date format. + + Parameters + ---------- + rdate : int + RDate date format. + + Returns + ------- + gyear : int + Gregorian year. + gmonth : int + Gregorian month. + gday : int + Gregorian day. + """ + + gyear = rdate_to_gyear(rdate) + priordays = rdate - gdate_to_rdate(gyear, 1, 1) + value1 = gdate_to_rdate(gyear, 3, 1) + if rdate < value1: + correction = 0 + elif rdate >= value1 and leap_year(gyear): + correction = 1 + else: + correction = 2 + gmonth = math.floor((12 * (priordays + correction) + 373) / 367) + gday = rdate - gdate_to_rdate(gyear, gmonth, 1) + 1 + return gyear, gmonth, gday + + +# +# Function rdate_to_gyear +# + +def rdate_to_gyear(rdate): + r"""Convert RDate format to Gregorian year. + + Parameters + ---------- + rdate : int + RDate date format. + + Returns + ------- + gyear : int + Gregorian year. + """ + + d0 = rdate - 1 + n400 = math.floor(d0 / 146097) + d1 = d0 % 146097 + n100 = math.floor(d1 / 36524) + d2 = d1 % 36524 + n4 = math.floor(d2 / 1461) + d3 = d2 % 1461 + n1 = math.floor(d3 / 365) + + theyear = 400 * n400 + 100 * n100 + 4 * n4 + n1 + if n100 == 4 or n1 == 4: + gyear = theyear + else: + gyear = theyear + 1 + return gyear + + +# +# Function set_events +# + +def set_events(n, k, gyear, gday): + r"""Define monthly events for a given year. + + Parameters + ---------- + n : int + Occurrence of a given day counting in either direction. + k : int + Day of the week. + gyear : int + Gregorian year for the events. + gday : int + Gregorian day representing the first day to consider. + + Returns + ------- + events : list of RDate (int) + Monthly events in RDate format. + + Example + ------- + >>> # Options Expiration (Third Friday of every month) + >>> set_events(3, 5, 2017, 1) + """ + + events = [] + month_range = range(1, 13) + for m in month_range: + rdate = nth_kday(n, k, gyear, m, gday) + events.append(rdate) + return events + + +# +# Function subtract_dates +# + +def subtract_dates(gyear1, gmonth1, gday1, gyear2, gmonth2, gday2): + r"""Calculate the difference between two Gregorian dates. + + Parameters + ---------- + gyear1 : int + Gregorian year of first date. + gmonth1 : int + Gregorian month of first date. + gday1 : int + Gregorian day of first date. + gyear2 : int + Gregorian year of successive date. + gmonth2 : int + Gregorian month of successive date. + gday2 : int + Gregorian day of successive date. + + Returns + ------- + delta_days : int + Difference in days in RDate format. + """ + delta_days = gdate_to_rdate(gyear2, gmonth2, gday2) \ + - gdate_to_rdate(gyear1, gmonth1, gday1) + return delta_days + + + +# +# Holiday Functions in Calendar Order +# + + +# +# Function new_years_day +# + +def new_years_day(gyear, observed): + r"""Get New Year's day for a given year. + + Parameters + ---------- + gyear : int + Gregorian year. + observed : bool + False if the exact date, True if the weekday. + + Returns + ------- + nyday : int + New Year's Day in RDate format. + """ + nyday = gdate_to_rdate(gyear, 1, 1) + if observed and day_of_week(nyday) == 0: + nyday += 1 + return nyday + + +# +# Function mlk_day +# + +def mlk_day(gyear): + r"""Get Martin Luther King Day for a given year. + + Parameters + ---------- + gyear : int + Gregorian year. + + Returns + ------- + mlkday : int + Martin Luther King Day in RDate format. + """ + mlkday = nth_kday(3, 1, gyear, 1, 1) + return mlkday + + +# +# Function valentines_day +# + +def valentines_day(gyear): + r"""Get Valentine's day for a given year. + + Parameters + ---------- + gyear : int + Gregorian year. + + Returns + ------- + valentines : int + Valentine's Day in RDate format. + """ + valentines = gdate_to_rdate(gyear, 2, 14) + return valentines + + +# +# Function presidents_day +# + +def presidents_day(gyear): + r"""Get President's Day for a given year. + + Parameters + ---------- + gyear : int + Gregorian year. + + Returns + ------- + prezday : int + President's Day in RDate format. + """ + prezday = nth_kday(3, 1, gyear, 2, 1) + return prezday + + +# +# Function saint_patricks_day +# + +def saint_patricks_day(gyear): + r"""Get Saint Patrick's day for a given year. + + Parameters + ---------- + gyear : int + Gregorian year. + observed : bool + False if the exact date, True if the weekday. + + Returns + ------- + patricks : int + Saint Patrick's Day in RDate format. + """ + patricks = gdate_to_rdate(gyear, 3, 17) + return patricks + + +# +# Function good_friday +# + +def good_friday(gyear): + r"""Get Good Friday for a given year. + + Parameters + ---------- + gyear : int + Gregorian year. + + Returns + ------- + gf : int + Good Friday in RDate format. + """ + gf = easter_day(gyear) - 2 + return gf + + +# +# Function easter_day +# + +def easter_day(gyear): + r"""Get Easter Day for a given year. + + Parameters + ---------- + gyear : int + Gregorian year. + + Returns + ------- + ed : int + Easter Day in RDate format. + """ + + century = math.floor(gyear / 100) + 1 + epacts = (14 + 11 * (gyear % 19) - math.floor(3 * century / 4) \ + + math.floor((5 + 8 * century) / 25)) % 30 + if epacts == 0 or (epacts == 1 and 10 < (gyear % 19)): + epacta = epacts + 1 + else: + epacta = epacts + rdate = gdate_to_rdate(gyear, 4, 19) - epacta + ed = kday_after(rdate, 0) + return ed + + +# +# Function cinco_de_mayo +# + +def cinco_de_mayo(gyear): + r"""Get Cinco de Mayo for a given year. + + Parameters + ---------- + gyear : int + Gregorian year. + + Returns + ------- + cinco_de_mayo : int + Cinco de Mayo in RDate format. + """ + cinco = gdate_to_rdate(gyear, 5, 5) + return cinco + + +# +# Function mothers_day +# + +def mothers_day(gyear): + r"""Get Mother's Day for a given year. + + Parameters + ---------- + gyear : int + Gregorian year. + + Returns + ------- + mothers_day : int + Mother's Day in RDate format. + """ + mothers_day = nth_kday(2, 0, gyear, 5, 1) + return mothers_day + + +# +# Function memorial_day +# + +def memorial_day(gyear): + r"""Get Memorial Day for a given year. + + Parameters + ---------- + gyear : int + Gregorian year. + + Returns + ------- + md : int + Memorial Day in RDate format. + """ + md = last_kday(1, gyear, 5, 31) + return md + + +# +# Function fathers_day +# + +def fathers_day(gyear): + r"""Get Father's Day for a given year. + + Parameters + ---------- + gyear : int + Gregorian year. + + Returns + ------- + fathers_day : int + Father's Day in RDate format. + """ + fathers_day = nth_kday(3, 0, gyear, 6, 1) + return fathers_day + + +# +# Function independence_day +# + +def independence_day(gyear, observed): + r"""Get Independence Day for a given year. + + Parameters + ---------- + gyear : int + Gregorian year. + observed : bool + False if the exact date, True if the weekday. + + Returns + ------- + d4j : int + Independence Day in RDate format. + """ + d4j = gdate_to_rdate(gyear, 7, 4) + if observed: + if day_of_week(d4j) == 6: + d4j -= 1 + if day_of_week(d4j) == 0: + d4j += 1 + return d4j + + +# +# Function labor_day +# + +def labor_day(gyear): + r"""Get Labor Day for a given year. + + Parameters + ---------- + gyear : int + Gregorian year. + + Returns + ------- + lday : int + Labor Day in RDate format. + """ + lday = first_kday(1, gyear, 9, 1) + return lday + + +# +# Function halloween +# + +def halloween(gyear): + r"""Get Halloween for a given year. + + Parameters + ---------- + gyear : int + Gregorian year. + + Returns + ------- + halloween : int + Halloween in RDate format. + """ + halloween = gdate_to_rdate(gyear, 10, 31) + return halloween + + +# +# Function veterans_day +# + +def veterans_day(gyear, observed): + r"""Get Veteran's day for a given year. + + Parameters + ---------- + gyear : int + Gregorian year. + observed : bool + False if the exact date, True if the weekday. + + Returns + ------- + veterans : int + Veteran's Day in RDate format. + """ + veterans = gdate_to_rdate(gyear, 11, 11) + if observed and day_of_week(veterans) == 0: + veterans += 1 + return veterans + + +# +# Function thanksgiving_day +# + +def thanksgiving_day(gyear): + r"""Get Thanksgiving Day for a given year. + + Parameters + ---------- + gyear : int + Gregorian year. + + Returns + ------- + tday : int + Thanksgiving Day in RDate format. + """ + tday = nth_kday(4, 4, gyear, 11, 1) + return tday + + +# +# Function christmas_day +# + +def christmas_day(gyear, observed): + r"""Get Christmas Day for a given year. + + Parameters + ---------- + gyear : int + Gregorian year. + observed : bool + False if the exact date, True if the weekday. + + Returns + ------- + xmas : int + Christmas Day in RDate format. + """ + xmas = gdate_to_rdate(gyear, 12, 25) + if observed: + if day_of_week(xmas) == 6: + xmas -= 1 + if day_of_week(xmas) == 0: + xmas += 1 + return xmas + + +# +# Define holiday map +# + +holiday_map = {"New Year's Day" : (new_years_day, True), + "MLK Day" : (mlk_day, False), + "Valentine's Day" : (valentines_day, False), + "President's Day" : (presidents_day, False), + "St. Patrick's Day" : (saint_patricks_day, False), + "Good Friday" : (good_friday, False), + "Easter" : (easter_day, False), + "Cinco de Mayo" : (cinco_de_mayo, False), + "Mother's Day" : (mothers_day, False), + "Memorial Day" : (memorial_day, False), + "Father's Day" : (fathers_day, False), + "Independence Day" : (independence_day, True), + "Labor Day" : (labor_day, False), + "Halloween" : (halloween, False), + "Veteran's Day" : (veterans_day, True), + "Thanksgiving" : (thanksgiving_day, False), + "Christmas" : (christmas_day, True)} + + +# +# Function get_holiday_names +# + +def get_holiday_names(): + r"""Get the list of defined holidays. + + Returns + ------- + holidays : list of str + List of holiday names. + """ + holidays = [h for h in holiday_map] + return holidays + + +# +# Function set_holidays +# + +def set_holidays(gyear, observe): + r"""Determine if this is a Gregorian leap year. + + Parameters + ---------- + gyear : int + Value for the corresponding key. + observe : bool + True to get the observed date, otherwise False. + + Returns + ------- + holidays : dict of int + Set of holidays in RDate format for a given year. + """ + + holidays = {} + for h in holiday_map: + hfunc = holiday_map[h][0] + observed = holiday_map[h][1] + if observed: + holidays[h] = hfunc(gyear, observe) + else: + holidays[h] = hfunc(gyear) + return holidays From c7602b7be7538aac872e024a9bbf8c9abd0d8b29 Mon Sep 17 00:00:00 2001 From: Mark Conway Date: Sun, 15 Mar 2020 10:22:33 -0400 Subject: [PATCH 075/129] consolidate transformer functions into transforms.py 1. Rename market_variables.py -> variables.py to keep Variable functionality only. 2. Functions from market_variables.py and features.py have been moved to new file transforms.py 3. Delete market_variables.py --- alphapy/__main__.py | 12 +- .../examples/Trading Model/config/model.yml | 24 +- alphapy/features.py | 326 +-- alphapy/market_flow.py | 4 +- alphapy/model.py | 10 +- alphapy/system.py | 2 +- .../{market_variables.py => transforms.py} | 1748 ++++++++--------- alphapy/variables.py | 612 ++++++ 8 files changed, 1432 insertions(+), 1306 deletions(-) rename alphapy/{market_variables.py => transforms.py} (67%) create mode 100644 alphapy/variables.py diff --git a/alphapy/__main__.py b/alphapy/__main__.py index 0e4324b..50230f6 100644 --- a/alphapy/__main__.py +++ b/alphapy/__main__.py @@ -31,7 +31,7 @@ from alphapy.data import shuffle_data from alphapy.estimators import get_estimators from alphapy.estimators import scorers -from alphapy.features import apply_treatments +from alphapy.features import apply_transforms from alphapy.features import create_crosstabs from alphapy.features import create_features from alphapy.features import create_interactions @@ -167,8 +167,8 @@ def training_pipeline(model): raise IndexError("The number of training and test columns [%d, %d] must match." % (X_train.shape[1], X_test.shape[1])) - # Apply treatments to the feature matrix - X_all = apply_treatments(model, X_all) + # Apply transforms to the feature matrix + X_all = apply_transforms(model, X_all) # Drop features X_all = drop_features(X_all, drop) @@ -341,8 +341,8 @@ def prediction_pipeline(model): logger.info("Number of Prediction Rows : %d", X_predict.shape[0]) logger.info("Number of Prediction Columns : %d", X_predict.shape[1]) - # Apply treatments to the feature matrix - X_all = apply_treatments(model, X_predict) + # Apply transforms to the feature matrix + X_all = apply_transforms(model, X_predict) # Drop features X_all = drop_features(X_all, drop) @@ -454,7 +454,7 @@ def main(args=None): # Logging logging.basicConfig(format="[%(asctime)s] %(levelname)s\t%(message)s", - filename="alphapy.log", filemode='a', level=logging.DEBUG, + filename="alphapy.log", filemode='a', level=logging.INFO, datefmt='%m/%d/%y %H:%M:%S') formatter = logging.Formatter("[%(asctime)s] %(levelname)s\t%(message)s", datefmt='%m/%d/%y %H:%M:%S') diff --git a/alphapy/examples/Trading Model/config/model.yml b/alphapy/examples/Trading Model/config/model.yml index 61f20f2..4f11f4b 100644 --- a/alphapy/examples/Trading Model/config/model.yml +++ b/alphapy/examples/Trading Model/config/model.yml @@ -95,18 +95,18 @@ features: threshold : 0.1 treatments: - doji : ['alphapy.features', 'runs_test', ['all'], 18] - hc : ['alphapy.features', 'runs_test', ['all'], 18] - hh : ['alphapy.features', 'runs_test', ['all'], 18] - hl : ['alphapy.features', 'runs_test', ['all'], 18] - ho : ['alphapy.features', 'runs_test', ['all'], 18] - rrhigh : ['alphapy.features', 'runs_test', ['all'], 18] - rrlow : ['alphapy.features', 'runs_test', ['all'], 18] - rrover : ['alphapy.features', 'runs_test', ['all'], 18] - rrunder : ['alphapy.features', 'runs_test', ['all'], 18] - sephigh : ['alphapy.features', 'runs_test', ['all'], 18] - seplow : ['alphapy.features', 'runs_test', ['all'], 18] - trend : ['alphapy.features', 'runs_test', ['all'], 18] + doji : ['alphapy.transforms', 'runs_test', ['all'], 18] + hc : ['alphapy.transforms', 'runs_test', ['all'], 18] + hh : ['alphapy.transforms', 'runs_test', ['all'], 18] + hl : ['alphapy.transforms', 'runs_test', ['all'], 18] + ho : ['alphapy.transforms', 'runs_test', ['all'], 18] + rrhigh : ['alphapy.transforms', 'runs_test', ['all'], 18] + rrlow : ['alphapy.transforms', 'runs_test', ['all'], 18] + rrover : ['alphapy.transforms', 'runs_test', ['all'], 18] + rrunder : ['alphapy.transforms', 'runs_test', ['all'], 18] + sephigh : ['alphapy.transforms', 'runs_test', ['all'], 18] + seplow : ['alphapy.transforms', 'runs_test', ['all'], 18] + trend : ['alphapy.transforms', 'runs_test', ['all'], 18] pipeline: number_jobs : -1 diff --git a/alphapy/features.py b/alphapy/features.py index 38ae4b6..f004482 100644 --- a/alphapy/features.py +++ b/alphapy/features.py @@ -31,8 +31,8 @@ from alphapy.globals import Encoders from alphapy.globals import ModelType from alphapy.globals import Scalers -from alphapy.market_variables import Variable -from alphapy.market_variables import vparse +from alphapy.variables import Variable +from alphapy.variables import vparse import category_encoders as ce from importlib import import_module @@ -108,287 +108,11 @@ # -# Function rtotal +# Function apply_transform # -def rtotal(vec): - r"""Calculate the running total. - - Parameters - ---------- - vec : pandas.Series - The input array for calculating the running total. - - Returns - ------- - running_total : int - The final running total. - - Example - ------- - - >>> vec.rolling(window=20).apply(rtotal) - - """ - tcount = np.count_nonzero(vec) - fcount = len(vec) - tcount - running_total = tcount - fcount - return running_total - - -# -# Function runs -# - -def runs(vec): - r"""Calculate the total number of runs. - - Parameters - ---------- - vec : pandas.Series - The input array for calculating the number of runs. - - Returns - ------- - runs_value : int - The total number of runs. - - Example - ------- - - >>> vec.rolling(window=20).apply(runs) - - """ - runs_value = len(list(itertools.groupby(vec))) - return runs_value - - -# -# Function streak -# - -def streak(vec): - r"""Determine the length of the latest streak. - - Parameters - ---------- - vec : pandas.Series - The input array for calculating the latest streak. - - Returns - ------- - latest_streak : int - The length of the latest streak. - - Example - ------- - - >>> vec.rolling(window=20).apply(streak) - - """ - latest_streak = [len(list(g)) for k, g in itertools.groupby(vec)][-1] - return latest_streak - - -# -# Function zscore -# - -def zscore(vec): - r"""Calculate the Z-Score. - - Parameters - ---------- - vec : pandas.Series - The input array for calculating the Z-Score. - - Returns - ------- - zscore : float - The value of the Z-Score. - - References - ---------- - To calculate the Z-Score, you can find more information here [ZSCORE]_. - - .. [ZSCORE] https://en.wikipedia.org/wiki/Standard_score - - Example - ------- - - >>> vec.rolling(window=20).apply(zscore) - - """ - n1 = np.count_nonzero(vec) - n2 = len(vec) - n1 - fac1 = float(2 * n1 * n2) - fac2 = float(n1 + n2) - rbar = fac1 / fac2 + 1 - sr2num = fac1 * (fac1 - n1 - n2) - sr2den = math.pow(fac2, 2) * (fac2 - 1) - sr = math.sqrt(sr2num / sr2den) - if sr2den and sr: - zscore = (runs(vec) - rbar) / sr - else: - zscore = 0 - return zscore - - -# -# Function runs_test -# - -def runs_test(f, c, wfuncs, window): - r"""Perform a runs test on binary series. - - Parameters - ---------- - f : pandas.DataFrame - Dataframe containing the column ``c``. - c : str - Name of the column in the dataframe ``f``. - wfuncs : list - The set of runs test functions to apply to the column: - - ``'all'``: - Run all of the functions below. - ``'rtotal'``: - The running total over the ``window`` period. - ``'runs'``: - Total number of runs in ``window``. - ``'streak'``: - The length of the latest streak. - ``'zscore'``: - The Z-Score over the ``window`` period. - window : int - The rolling period. - - Returns - ------- - new_features : pandas.DataFrame - The dataframe containing the runs test features. - - References - ---------- - For more information about runs tests for detecting non-randomness, - refer to [RUNS]_. - - .. [RUNS] http://www.itl.nist.gov/div898/handbook/eda/section3/eda35d.htm - - """ - - fc = f[c] - all_funcs = {'runs' : runs, - 'streak' : streak, - 'rtotal' : rtotal, - 'zscore' : zscore} - # use all functions - if 'all' in wfuncs: - wfuncs = list(all_funcs.keys()) - # apply each of the runs functions - new_features = pd.DataFrame() - for w in wfuncs: - if w in all_funcs: - new_feature = fc.rolling(window=window).apply(all_funcs[w]) - new_feature.fillna(0, inplace=True) - new_column_name = PSEP.join([c, w]) - new_feature = new_feature.rename(new_column_name) - frames = [new_features, new_feature] - new_features = pd.concat(frames, axis=1) - else: - logger.info("Runs Function %s not found", w) - return new_features - - -# -# Function split_to_letters -# - -def split_to_letters(f, c): - r"""Separate text into distinct characters. - - Parameters - ---------- - f : pandas.DataFrame - Dataframe containing the column ``c``. - c : str - Name of the text column in the dataframe ``f``. - - Returns - ------- - new_feature : pandas.Series - The array containing the new feature. - - Example - ------- - The value 'abc' becomes 'a b c'. - - """ - fc = f[c] - new_feature = None - dtype = fc.dtypes - if dtype == 'object': - fc.fillna(NULLTEXT, inplace=True) - maxlen = fc.str.len().max() - if maxlen > 1: - new_feature = fc.apply(lambda x: BSEP.join(list(x))) - return new_feature - - -# -# Function texplode -# - -def texplode(f, c): - r"""Get dummy values for a text column. - - Parameters - ---------- - f : pandas.DataFrame - Dataframe containing the column ``c``. - c : str - Name of the text column in the dataframe ``f``. - - Returns - ------- - dummies : pandas.DataFrame - The dataframe containing the dummy variables. - - Example - ------- - - This function is useful for columns that appear to - have separate character codes but are consolidated - into a single column. Here, the column ``c`` is - transformed into five dummy variables. - - === === === === === === - c 0_a 1_x 1_b 2_x 2_z - === === === === === === - abz 1 0 1 0 1 - abz 1 0 1 0 1 - axx 1 1 0 1 0 - abz 1 0 1 0 1 - axz 1 1 0 0 1 - === === === === === === - - """ - fc = f[c] - maxlen = fc.str.len().max() - fc.fillna(maxlen * BSEP, inplace=True) - fpad = str().join(['{:', BSEP, '>', str(maxlen), '}']) - fcpad = fc.apply(fpad.format) - fcex = fcpad.apply(lambda x: pd.Series(list(x))) - dummies = pd.get_dummies(fcex) - return dummies - - -# -# Function apply_treatment -# - -def apply_treatment(fname, df, fparams): - r"""Apply a treatment function to a column of the dataframe. +def apply_transform(fname, df, fparams): + r"""Apply a transform function to a column of the dataframe. Parameters ---------- @@ -397,62 +121,62 @@ def apply_treatment(fname, df, fparams): df : pandas.DataFrame Dataframe containing the column ``fname``. fparams : list - The module, function, and parameter list of the treatment + The module, function, and parameter list of the transform function Returns ------- new_features : pandas.DataFrame - The set of features after applying a treatment function. + The set of features after applying a transform function. """ - # Extract the treatment parameter list + # Extract the transform parameter list module = fparams[0] func_name = fparams[1] plist = fparams[2:] # Append to system path sys.path.append(os.getcwd()) - # Import the external treatment function + # Import the external transform function ext_module = import_module(module) func = getattr(ext_module, func_name) # Prepend the parameter list with the data frame and feature name plist.insert(0, fname) plist.insert(0, df) - # Apply the treatment + # Apply the transform logger.info("Applying function %s from module %s to feature %s", func_name, module, fname) return func(*plist) # -# Function apply_treatments +# Function apply_transforms # -def apply_treatments(model, X): +def apply_transforms(model, X): r"""Apply special functions to the original features. Parameters ---------- model : alphapy.Model - Model specifications indicating any treatments. + Model specifications indicating any transforms. X : pandas.DataFrame Combined train and test data, or just prediction data. Returns ------- all_features : pandas.DataFrame - All features, including treatments. + All features, including transforms. Raises ------ IndexError - The number of treatment rows must match the number of + The number of transform rows must match the number of rows in ``X``. """ # Extract model parameters - treatments = model.specs['treatments'] + transforms = model.specs['transforms'] # Log input parameters @@ -461,11 +185,11 @@ def apply_treatments(model, X): # Iterate through columns, dispatching and transforming each feature. - logger.info("Applying Treatments") + logger.info("Applying transforms") all_features = X - if treatments: - for fname in treatments: + if transforms: + for fname in transforms: # find feature series fcols = [] for col in X.columns: @@ -476,22 +200,22 @@ def apply_treatments(model, X): for item in fcols: _, _, _, lag = vparse(item) lag_values.append(lag) - # apply treatment to the most recent value + # apply transform to the most recent value if lag_values: f_latest = fcols[lag_values.index(min(lag_values))] - features = apply_treatment(f_latest, X, treatments[fname]) + features = apply_transform(f_latest, X, transforms[fname]) if features is not None: if features.shape[0] == X.shape[0]: all_features = pd.concat([all_features, features], axis=1) else: - raise IndexError("The number of treatment rows [%d] must match X [%d]" % + raise IndexError("The number of transform rows [%d] must match X [%d]" % (features.shape[0], X.shape[0])) else: - logger.info("Could not apply treatment for feature %s", fname) + logger.info("Could not apply transform for feature %s", fname) else: - logger.info("Feature %s is missing for treatment", fname) + logger.info("Feature %s is missing for transform", fname) else: - logger.info("No Treatments Specified") + logger.info("No transforms Specified") logger.info("New Feature Count : %d", all_features.shape[1]) diff --git a/alphapy/market_flow.py b/alphapy/market_flow.py index 37627d9..2c7fcf6 100644 --- a/alphapy/market_flow.py +++ b/alphapy/market_flow.py @@ -33,8 +33,8 @@ from alphapy.globals import PD_INTRADAY_OFFSETS from alphapy.globals import PSEP, SSEP from alphapy.group import Group -from alphapy.market_variables import Variable -from alphapy.market_variables import vmapply +from alphapy.variables import Variable +from alphapy.variables import vmapply from alphapy.model import get_model_config from alphapy.model import Model from alphapy.portfolio import gen_portfolio diff --git a/alphapy/model.py b/alphapy/model.py index 6123438..9346a3b 100644 --- a/alphapy/model.py +++ b/alphapy/model.py @@ -367,13 +367,13 @@ def get_model_config(): specs['learning_curve'] = cfg['plots']['learning_curve'] specs['roc_curve'] = cfg['plots']['roc_curve'] - # Section: treatments + # Section: transforms try: - specs['treatments'] = cfg['treatments'] + specs['transforms'] = cfg['transforms'] except: - specs['treatments'] = None - logger.info("No Treatments Found") + specs['transforms'] = None + logger.info("No transforms Found") # Section: xgboost @@ -451,7 +451,7 @@ def get_model_config(): logger.info('submit_probas = %r', specs['submit_probas']) logger.info('target [y] = %s', specs['target']) logger.info('target_value = %d', specs['target_value']) - logger.info('treatments = %s', specs['treatments']) + logger.info('transforms = %s', specs['transforms']) logger.info('tsne = %r', specs['tsne']) logger.info('tsne_components = %d', specs['tsne_components']) logger.info('tsne_learn_rate = %f', specs['tsne_learn_rate']) diff --git a/alphapy/system.py b/alphapy/system.py index 6930ab9..f9a5562 100644 --- a/alphapy/system.py +++ b/alphapy/system.py @@ -32,7 +32,7 @@ from alphapy.frame import write_frame from alphapy.globals import Orders from alphapy.globals import BSEP, SSEP -from alphapy.market_variables import vexec +from alphapy.variables import vexec from alphapy.space import Space from alphapy.portfolio import Trade from alphapy.utilities import most_recent_file diff --git a/alphapy/market_variables.py b/alphapy/transforms.py similarity index 67% rename from alphapy/market_variables.py rename to alphapy/transforms.py index 77ee8ea..fd3cf76 100644 --- a/alphapy/market_variables.py +++ b/alphapy/transforms.py @@ -1,10 +1,10 @@ ################################################################################ # # Package : AlphaPy -# Module : market_variables -# Created : July 11, 2013 +# Module : transforms +# Created : March 14, 2020 # -# Copyright 2017 ScottFree Analytics LLC +# Copyright 2020 ScottFree Analytics LLC # Mark Conway & Robert D. Scott II # # Licensed under the Apache License, Version 2.0 (the "License"); @@ -22,43 +22,21 @@ ################################################################################ -# -# Variables -# --------- -# -# Numeric substitution is allowed for any number in the expression. -# Offsets are allowed in event expressions but cannot be substituted. -# -# Examples -# -------- -# -# Variable('rrunder', 'rr_3_20 <= 0.9') -# -# 'rrunder_2_10_0.7' -# 'rrunder_2_10_0.9' -# 'xmaup_20_50_20_200' -# 'xmaup_10_50_20_50' -# - - # # Imports # -from alphapy.alias import get_alias -from alphapy.frame import Frame -from alphapy.frame import frame_name -from alphapy.globals import BSEP, LOFF, ROFF, USEP -from alphapy.utilities import valid_name +from alphapy.calendrical import biz_day_month +from alphapy.calendrical import biz_day_week +from alphapy.globals import NULLTEXT +from alphapy.globals import BSEP, PSEP, USEP +from alphapy.variables import vexec -from collections import OrderedDict -from importlib import import_module +import itertools import logging +import math import numpy as np import pandas as pd -import parser -import re -import sys # @@ -69,626 +47,332 @@ # -# Class Variable +# Function abovema # -class Variable(object): - """Create a new variable as a key-value pair. All variables are stored - in ``Variable.variables``. Duplicate keys or values are not allowed, - unless the ``replace`` parameter is ``True``. +def abovema(f, c, p = 50): + r"""Determine those values of the dataframe that are above the + moving average. Parameters ---------- - name : str - Variable key. - expr : str - Variable value. - replace : bool, optional - Replace the current key-value pair if it already exists. - - Attributes - ---------- - variables : dict - Class variable for storing all known variables + f : pandas.DataFrame + Dataframe containing the column ``c``. + c : str + Name of the column in the dataframe ``f``. + p : int + The period of the moving average. - Examples - -------- - - >>> Variable('rrunder', 'rr_3_20 <= 0.9') - >>> Variable('hc', 'higher_close') + Returns + ------- + new_column : pandas.Series (bool) + The array containing the new feature. """ - - # class variable to track all variables - - variables = {} - - # function __new__ - - def __new__(cls, - name, - expr, - replace = False): - # code - efound = expr in [Variable.variables[key].expr for key in Variable.variables] - if efound: - key = [key for key in Variable.variables if expr in Variable.variables[key].expr] - logger.info("Expression '%s' already exists for key %s", expr, key) - return - else: - if replace or not name in Variable.variables: - if not valid_name(name): - logger.info("Invalid variable key: %s", name) - return - try: - result = parser.expr(expr) - except: - logger.info("Invalid expression: %s", expr) - return - return super(Variable, cls).__new__(cls) - else: - logger.info("Key %s already exists", name) - - # function __init__ - - def __init__(self, - name, - expr, - replace = False): - # code - self.name = name; - self.expr = expr; - # add key with expression - Variable.variables[name] = self - - # function __str__ - - def __str__(self): - return self.expr + new_column = f[c] > ma(f, c, p) + return new_column # -# Function vparse +# Function adx # -def vparse(vname): - r"""Parse a variable name into its respective components. +def adx(f, p = 14): + r"""Calculate the Average Directional Index (ADX). Parameters ---------- - vname : str - The name of the variable. + 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 ------- - vxlag : str - Variable name without the ``lag`` component. - root : str - The base variable name without the parameters. - plist : list - The parameter list. - lag : int - The offset starting with the current value [0] - and counting back, e.g., an offset [1] means the - previous value of the variable. - - Notes - ----- - - **AlphaPy** makes feature creation easy. The syntax - of a variable name maps to a function call: - - xma_20_50 => xma(20, 50) - - Examples - -------- - - >>> vparse('xma_20_50[1]') - # ('xma_20_50', 'xma', ['20', '50'], 1) - - """ - - # split along lag first - lsplit = vname.split(LOFF) - vxlag = lsplit[0] - # if necessary, substitute any alias - root = vxlag.split(USEP)[0] - alias = get_alias(root) - if alias: - vxlag = vxlag.replace(root, alias) - vsplit = vxlag.split(USEP) - root = vsplit[0] - plist = vsplit[1:] - # extract lag - lag = 0 - if len(lsplit) > 1: - # lag is present - slag = lsplit[1].replace(ROFF, '') - if len(slag) > 0: - lpat = r'(^-?[0-9]+$)' - lre = re.compile(lpat) - if lre.match(slag): - lag = int(slag) - # return all components - return vxlag, root, plist, lag - - -# -# Function allvars -# - -def allvars(expr): - r"""Get the list of valid names in the expression. + new_column : pandas.Series (float) + The array containing the new feature. - Parameters + References ---------- - expr : str - A valid expression conforming to the Variable Definition Language. + 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. - Returns - ------- - vlist : list - List of valid variable names. + .. [WIKI_ADX] https://en.wikipedia.org/wiki/Average_directional_movement_index """ - regex = re.compile('\w+') - items = regex.findall(expr) - vlist = [] - for item in items: - if valid_name(item): - vlist.append(item) - return vlist + 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 # -# Function vtree +# Function belowma # -def vtree(vname): - r"""Get all of the antecedent variables. - - Before applying a variable to a dataframe, we have to recursively - get all of the child variables, beginning with the starting variable's - expression. Then, we have to extract the variables from all the - subsequent expressions. This process continues until all antecedent - variables are obtained. +def belowma(f, c, p = 50): + r"""Determine those values of the dataframe that are below the + moving average. Parameters ---------- - vname : str - A valid variable stored in ``Variable.variables``. + f : pandas.DataFrame + Dataframe containing the column ``c``. + c : str + Name of the column in the dataframe ``f``. + p : int + The period of the moving average. Returns ------- - all_variables : list - The variables that need to be applied before ``vname``. - - Other Parameters - ---------------- - Variable.variables : dict - Global dictionary of variables + new_column : pandas.Series (bool) + The array containing the new feature. """ - allv = [] - def vwalk(allv, vname): - vxlag, root, plist, lag = vparse(vname) - if root in Variable.variables: - root_expr = Variable.variables[root].expr - expr = vsub(vname, root_expr) - av = allvars(expr) - for v in av: - vwalk(allv, v) - else: - for p in plist: - if valid_name(p): - vwalk(allv, p) - allv.append(vname) - return allv - allv = vwalk(allv, vname) - all_variables = list(OrderedDict.fromkeys(allv)) - return all_variables + new_column = f[c] < ma(f, c, p) + return new_column # -# Function vsub +# Function c2max # - -def vsub(v, expr): - r"""Substitute the variable parameters into the expression. - - This function performs the parameter substitution when - applying features to a dataframe. It is a mechanism for - the user to override the default values in any given - expression when defining a feature, instead of having - to programmatically call a function with new values. + +def c2max(f, c1, c2): + r"""Take the maximum value between two columns in a dataframe. Parameters ---------- - v : str - Variable name. - expr : str - The expression for substitution. + f : pandas.DataFrame + Dataframe containing the two columns ``c1`` and ``c2``. + c1 : str + Name of the first column in the dataframe ``f``. + c2 : str + Name of the second column in the dataframe ``f``. Returns ------- - newexpr - The expression with the new, substituted values. + max_val : float + The maximum value of the two columns. """ - # numbers pattern - npat = '[-+]?[0-9]*\.?[0-9]+' - nreg = re.compile(npat) - # find all number locations in variable name - vnums = nreg.findall(v) - viter = nreg.finditer(v) - vlocs = [] - for match in viter: - vlocs.append(match.span()) - # find all number locations in expression - # find all non-number locations as well - elen = len(expr) - enums = nreg.findall(expr) - eiter = nreg.finditer(expr) - elocs = [] - enlocs = [] - index = 0 - for match in eiter: - eloc = match.span() - elocs.append(eloc) - enlocs.append((index, eloc[0])) - index = eloc[1] - # build new expression - newexpr = str() - for i, enloc in enumerate(enlocs): - if i < len(vlocs): - newexpr += expr[enloc[0]:enloc[1]] + v[vlocs[i][0]:vlocs[i][1]] - else: - newexpr += expr[enloc[0]:enloc[1]] + expr[elocs[i][0]:elocs[i][1]] - if elocs: - estart = elocs[len(elocs)-1][1] - else: - estart = 0 - newexpr += expr[estart:elen] - return newexpr + max_val = max(f[c1], f[c2]) + return max_val + - # -# Function vexec +# Function c2min # - -def vexec(f, v, vfuncs=None): - r"""Add a variable to the given dataframe. - - This is the core function for adding a variable to a dataframe. - The default variable functions are already defined locally - in ``alphapy.var``; however, you may want to define your - own variable functions. If so, then the ``vfuncs`` parameter - will contain the list of modules and functions to be imported - and applied by the ``vexec`` function. - - To write your own variable function, your function must have - a pandas *DataFrame* as an input parameter and must return - a pandas *Series* that represents the new variable. + +def c2min(f, c1, c2): + r"""Take the minimum value between two columns in a dataframe. Parameters ---------- f : pandas.DataFrame - Dataframe to contain the new variable. - v : str - Variable to add to the dataframe. - vfuncs : dict, optional - Dictionary of external modules and functions. + Dataframe containing the two columns ``c1`` and ``c2``. + c1 : str + Name of the first column in the dataframe ``f``. + c2 : str + Name of the second column in the dataframe ``f``. Returns ------- - f : pandas.DataFrame - Dataframe with the new variable. - - Other Parameters - ---------------- - Variable.variables : dict - Global dictionary of variables + min_val : float + The minimum value of the two columns. """ - vxlag, root, plist, lag = vparse(v) - logger.debug("vexec : %s", v) - logger.debug("vxlag : %s", vxlag) - logger.debug("root : %s", root) - logger.debug("plist : %s", plist) - logger.debug("lag : %s", lag) - if vxlag not in f.columns: - if root in Variable.variables: - logger.debug("Found variable %s: ", root) - vroot = Variable.variables[root] - expr = vroot.expr - expr_new = vsub(vxlag, expr) - estr = "%s" % expr_new - logger.debug("Expression: %s", estr) - # pandas eval - f[vxlag] = f.eval(estr) - else: - logger.debug("Did not find variable: %s", root) - # Must be a function call - func_name = root - # Convert the parameter list and prepend the data frame - newlist = [] - for p in plist: - try: - newlist.append(int(p)) - except: - try: - newlist.append(float(p)) - except: - newlist.append(p) - newlist.insert(0, f) - # Find the module and function - module = None - if vfuncs: - for m in vfuncs: - funcs = vfuncs[m] - if func_name in funcs: - module = m - break - # If the module was found, import the external treatment function, - # else search the local namespace. - if module: - ext_module = import_module(module) - func = getattr(my_module, func_name) - # Create the variable by calling the function - f[v] = func(*newlist) - else: - modname = globals()['__name__'] - module = sys.modules[modname] - if func_name in dir(module): - func = getattr(module, func_name) - # Create the variable - f[v] = func(*newlist) - else: - logger.debug("Could not find function %s", func_name) - # if necessary, add the lagged variable - if lag > 0 and vxlag in f.columns: - f[v] = f[vxlag].shift(lag) - # output frame - return f - - -# -# Function vapply -# - -def vapply(group, vname, vfuncs=None): - r"""Apply a variable to multiple dataframes. - - Parameters - ---------- - group : alphapy.Group - The input group. - vname : str - The variable to apply to the ``group``. - vfuncs : dict, optional - Dictionary of external modules and functions. - - Returns - ------- - None : None - - Other Parameters - ---------------- - Frame.frames : dict - Global dictionary of dataframes + min_val = min(f[c1], f[c2]) + return min_val - See Also - -------- - vunapply - - """ - # get all frame names to apply variables - gnames = [item.lower() for item in group.members] - # get all the precedent variables - allv = vtree(vname) - # apply the variables to each frame - for g in gnames: - fname = frame_name(g, group.space) - if fname in Frame.frames: - f = Frame.frames[fname].df - if not f.empty: - for v in allv: - logger.debug("Applying variable %s to %s", v, g) - f = vexec(f, v, vfuncs) - else: - logger.debug("Frame for %s is empty", g) - else: - logger.debug("Frame not found: %s", fname) - # -# Function vmapply +# Function diff # -def vmapply(group, vs, vfuncs=None): - r"""Apply multiple variables to multiple dataframes. +def diff(f, c, n = 1): + r"""Calculate the n-th order difference for the given variable. Parameters ---------- - group : alphapy.Group - The input group. - vs : list - The list of variables to apply to the ``group``. - vfuncs : dict, optional - Dictionary of external modules and functions. + f : pandas.DataFrame + Dataframe containing the column ``c``. + c : str + Name of the column in the dataframe ``f``. + n : int + The number of times that the values are differenced. Returns ------- - None : None - - See Also - -------- - vmunapply + new_column : pandas.Series (float) + The array containing the new feature. """ - for v in vs: - logger.info("Applying variable: %s", v) - vapply(group, v, vfuncs) + new_column = np.diff(f[c], n) + return new_column + - # -# Function vunapply +# Function diminus # -def vunapply(group, vname): - r"""Remove a variable from multiple dataframes. +def diminus(f, p = 14): + r"""Calculate the Minus Directional Indicator (-DI). Parameters ---------- - group : alphapy.Group - The input group. - vname : str - The variable to remove from the ``group``. + f : pandas.DataFrame + Dataframe with columns ``high`` and ``low``. + p : int + The period over which to calculate the -DI. Returns ------- - None : None + new_column : pandas.Series (float) + The array containing the new feature. - Other Parameters - ---------------- - Frame.frames : dict - Global dictionary of dataframes + 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]_. - See Also - -------- - vapply + .. [IP_NDI] http://www.investopedia.com/terms/n/negativedirectionalindicator.asp """ - # get all frame names to apply variables - gnames = [item.lower() for item in group.all_members()] - # apply the variables to each frame - for g in gnames: - fname = frame_name(g, group.space) - if fname in Frame.frames: - f = Frame.frames[fname].df - logger.info("Unapplying variable %s from %s", vname, g) - if vname not in f.columns: - logger.info("Variable %s not in %s frame", vname, g) - else: - estr = "Frame.frames['%s'].df = f.df.drop('%s', axis=1)" \ - % (fname, vname) - exec(estr) - else: - logger.info("Frame not found: %s", fname) - + 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 + # -# Function vmunapply +# Function diplus # -def vmunapply(group, vs): - r"""Remove a list of variables from multiple dataframes. +def diplus(f, p = 14): + r"""Calculate the Plus Directional Indicator (+DI). Parameters ---------- - group : alphapy.Group - The input group. - vs : list - The list of variables to remove from the ``group``. + f : pandas.DataFrame + Dataframe with columns ``high`` and ``low``. + p : int + The period over which to calculate the +DI. Returns ------- - None : None + new_column : pandas.Series (float) + The array containing the new feature. - See Also - -------- - vmapply + References + ---------- + *A component of the average directional index (ADX) that is used to + measure the presence of an uptrend. When the +DI is sloping upward, + it is a signal that the uptrend is getting stronger* [IP_PDI]_. + + .. [IP_PDI] http://www.investopedia.com/terms/p/positivedirectionalindicator.asp """ - for v in vs: - vunapply(group, v) + tr = 'truerange' + vexec(f, tr) + atr = USEP.join(['atr', str(p)]) + vexec(f, atr) + dmp = 'dmplus' + vexec(f, dmp) + new_column = 100 * f[dmp].ewm(span=p).mean() / f[atr] + return new_column # -# This is the reference for all internal and external variable functions. -# -# -# 1. datetime functions -# -# date, datetime, time, timedelta -# -# 2. numpy unary ufuncs (PDA p. 96) -# -# abs, ceil, cos, exp, floor, log, log10, log2, modf, rint, sign, -# sin, square, sqrt, tan -# -# 3. moving window and exponential functions (PDA p. 323) -# -# rolling, ewm -# -# 5. pandas descriptive and summary statistical functions (PDA p. 139) -# -# argmin, argmax, count, cummax, cummin, cumprod, cumsum, describe, -# diff, idxmin, idxmax, kurt, mad, max, mean, median, min, pct_change, -# quantile, skew, std, sum, var -# -# 6. time series (PDA p. 289-328) +# Function dminus # - -# -# Function c2max -# - -def c2max(f, c1, c2): - r"""Take the maximum value between two columns in a dataframe. +def dminus(f): + r"""Calculate the Minus Directional Movement (-DM). Parameters ---------- f : pandas.DataFrame - Dataframe containing the two columns ``c1`` and ``c2``. - c1 : str - Name of the first column in the dataframe ``f``. - c2 : str - Name of the second column in the dataframe ``f``. + Dataframe with columns ``high`` and ``low``. Returns ------- - max_val : float - The maximum value of the two columns. + new_column : pandas.Series (float) + The array containing the new feature. + + References + ---------- + *Directional movement is negative (minus) when the prior low minus + the current low is greater than the current high minus the prior high. + This so-called Minus Directional Movement (-DM) equals the prior low + minus the current low, provided it is positive. A negative value + would simply be entered as zero* [SC_ADX]_. """ - max_val = max(f[c1], f[c2]) - return max_val + c1 = 'downmove' + f[c1] = -net(f, 'low') + c2 = 'upmove' + f[c2] = net(f, 'high') + new_column = f.apply(gtval0, axis=1, args=[c1, c2]) + return new_column # -# Function c2min +# Function dmplus # - -def c2min(f, c1, c2): - r"""Take the minimum value between two columns in a dataframe. + +def dmplus(f): + r"""Calculate the Plus Directional Movement (+DM). Parameters ---------- f : pandas.DataFrame - Dataframe containing the two columns ``c1`` and ``c2``. - c1 : str - Name of the first column in the dataframe ``f``. - c2 : str - Name of the second column in the dataframe ``f``. + Dataframe with columns ``high`` and ``low``. Returns ------- - min_val : float - The minimum value of the two columns. + new_column : pandas.Series (float) + The array containing the new feature. + + References + ---------- + *Directional movement is positive (plus) when the current high minus + the prior high is greater than the prior low minus the current low. + This so-called Plus Directional Movement (+DM) then equals the current + high minus the prior high, provided it is positive. A negative value + would simply be entered as zero* [SC_ADX]_. + + .. [SC_ADX] http://stockcharts.com/school/doku.php?id=chart_school:technical_indicators:average_directional_index_adx """ - min_val = min(f[c1], f[c2]) - return min_val + c1 = 'upmove' + f[c1] = net(f, 'high') + c2 = 'downmove' + f[c2] = -net(f, 'low') + new_column = f.apply(gtval0, axis=1, args=[c1, c2]) + return new_column # -# Function pchange1 +# Function down # - -def pchange1(f, c, o = 1): - r"""Calculate the percentage change within the same variable. + +def down(f, c): + r"""Find the negative values in the series. Parameters ---------- @@ -696,34 +380,30 @@ def pchange1(f, c, o = 1): Dataframe containing the column ``c``. c : str Name of the column in the dataframe ``f``. - o : int - Offset to the previous value. Returns ------- - new_column : pandas.Series (float) + new_column : pandas.Series (bool) The array containing the new feature. """ - new_column = f[c] / f[c].shift(o) - 1.0 + new_column = f[c] < 0 return new_column # -# Function pchange2 +# Function dpc # -def pchange2(f, c1, c2): - r"""Calculate the percentage change between two variables. +def dpc(f, c): + r"""Get the negative values, with positive values zeroed. Parameters ---------- f : pandas.DataFrame - Dataframe containing the two columns ``c1`` and ``c2``. - c1 : str - Name of the first column in the dataframe ``f``. - c2 : str - Name of the second column in the dataframe ``f``. + Dataframe with column ``c``. + c : str + Name of the column. Returns ------- @@ -731,16 +411,16 @@ def pchange2(f, c1, c2): The array containing the new feature. """ - new_column = f[c1] / f[c2] - 1.0 + new_column = f.apply(mval, axis=1, args=[c]) return new_column # -# Function diff +# Function ema # -def diff(f, c, n = 1): - r"""Calculate the n-th order difference for the given variable. +def ema(f, c, p = 20): + r"""Calculate the mean on a rolling basis. Parameters ---------- @@ -748,459 +428,474 @@ def diff(f, c, n = 1): Dataframe containing the column ``c``. c : str Name of the column in the dataframe ``f``. - n : int - The number of times that the values are differenced. + p : int + The period over which to calculate the rolling mean. Returns ------- new_column : pandas.Series (float) The array containing the new feature. + References + ---------- + *An exponential moving average (EMA) is a type of moving average + that is similar to a simple moving average, except that more weight + is given to the latest data* [IP_EMA]_. + + .. [IP_EMA] http://www.investopedia.com/terms/e/ema.asp + """ - new_column = np.diff(f[c], n) + new_column = pd.ewma(f[c], span=p) return new_column # -# Function down +# Function extract_bizday # -def down(f, c): - r"""Find the negative values in the series. +def extract_bizday(f, c): + r"""Extract business day of month and week. Parameters ---------- f : pandas.DataFrame - Dataframe containing the column ``c``. + Dataframe containing the date column ``c``. c : str - Name of the column in the dataframe ``f``. + Name of the date column in the dataframe ``f``. Returns ------- - new_column : pandas.Series (bool) - The array containing the new feature. - + date_features : pandas.DataFrame + The dataframe containing the date features. """ - new_column = f[c] < 0 - return new_column + + date_features = pd.DataFrame() + try: + date_features = extract_date(f, c) + rdate = date_features.apply(get_rdate, axis=1) + bdm = pd.Series(rdate.apply(biz_day_month), name='bizday_month') + bdw = pd.Series(rdate.apply(biz_day_week), name='bizday_week') + frames = [date_features, bdm, bdw] + date_features = pd.concat(frames, axis=1) + except: + logger.info("Could not extract business date information from %s column", c) + return date_features # -# Function up +# Function extract_date # -def up(f, c): - r"""Find the positive values in the series. +def extract_date(f, c): + r"""Extract date into its components: year, month, day. Parameters ---------- f : pandas.DataFrame - Dataframe containing the column ``c``. + Dataframe containing the date column ``c``. c : str - Name of the column in the dataframe ``f``. + Name of the date column in the dataframe ``f``. Returns ------- - new_column : pandas.Series (bool) - The array containing the new feature. - + date_features : pandas.DataFrame + The dataframe containing the date features. """ - new_column = f[c] > 0 - return new_column + + fc = pd.to_datetime(f[c]) + date_features = pd.DataFrame() + try: + fyear = pd.Series(fc.dt.year, name='year') + fmonth = pd.Series(fc.dt.month, name='month') + fday = pd.Series(fc.dt.day, name='day') + frames = [fyear, fmonth, fday] + date_features = pd.concat(frames, axis=1) + except: + logger.info("Could not extract date information from %s column", c) + return date_features # -# Function higher +# Function extract_time # -def higher(f, c, o = 1): - r"""Determine whether or not a series value is higher than - the value ``o`` periods back. +def extract_time(f, c): + r"""Extract time into its components: hour, minute, second. Parameters ---------- f : pandas.DataFrame - Dataframe containing the column ``c``. + Dataframe containing the time column ``c``. c : str - Name of the column in the dataframe ``f``. - o : int, optional - Offset value for shifting the series. + Name of the time column in the dataframe ``f``. Returns ------- - new_column : pandas.Series (bool) - The array containing the new feature. - + time_features : pandas.DataFrame + The dataframe containing the time features. """ - new_column = f[c] > f[c].shift(o) - return new_column + + fc = pd.to_datetime(f[c]) + time_features = pd.DataFrame() + try: + fhour = pd.Series(fc.dt.hour, name='year') + fminute = pd.Series(fc.dt.minute, name='month') + fsecond = pd.Series(fc.dt.second, name='day') + frames = [fhour, fminute, fsecond] + time_features = pd.concat(frames, axis=1) + except: + logger.info("Could not extract time information from %s column", c) + return time_features # -# Function highest +# Function gap # -def highest(f, c, p = 20): - r"""Calculate the highest value on a rolling basis. +def gap(f): + r"""Calculate the gap percentage between the current open and + the previous close. Parameters ---------- f : pandas.DataFrame - Dataframe containing the column ``c``. - c : str - Name of the column in the dataframe ``f``. - p : int - The period over which to calculate the rolling maximum. + 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 + """ - new_column = f[c].rolling(p).max() + c1 = 'open' + c2 = 'close[1]' + vexec(f, c2) + new_column = 100 * pchange2(f, c1, c2) return new_column # -# Function lower +# Function gapbadown # -def lower(f, c, o = 1): - r"""Determine whether or not a series value is lower than - the value ``o`` periods back. +def gapbadown(f): + r"""Determine whether or not there has been a breakaway gap down. Parameters ---------- f : pandas.DataFrame - Dataframe containing the column ``c``. - c : str - Name of the column in the dataframe ``f``. - o : int, optional - Offset value for shifting the series. + Dataframe with columns ``open`` and ``low``. Returns ------- new_column : pandas.Series (bool) The array containing the new feature. + References + ---------- + *A breakaway gap represents a gap in the movement of a stock price + supported by levels of high volume* [IP_BAGAP]_. + + .. [IP_BAGAP] http://www.investopedia.com/terms/b/breakawaygap.asp + """ - new_column = f[c] < f[c].shift(o) + new_column = f['open'] < f['low'].shift(1) return new_column # -# Function lowest +# Function gapbaup # -def lowest(f, c, p = 20): - r"""Calculate the lowest value on a rolling basis. +def gapbaup(f): + r"""Determine whether or not there has been a breakaway gap up. Parameters ---------- f : pandas.DataFrame - Dataframe containing the column ``c``. - c : str - Name of the column in the dataframe ``f``. - p : int - The period over which to calculate the rolling minimum. + Dataframe with columns ``open`` and ``high``. Returns ------- - new_column : pandas.Series (float) + new_column : pandas.Series (bool) The array containing the new feature. + References + ---------- + *A breakaway gap represents a gap in the movement of a stock price + supported by levels of high volume* [IP_BAGAP]_. + """ - return f[c].rolling(p).min() + new_column = f['open'] > f['high'].shift(1) + return new_column # -# Function ma +# Function gapdown # -def ma(f, c, p = 20): - r"""Calculate the mean on a rolling basis. +def gapdown(f): + r"""Determine whether or not there has been a gap down. Parameters ---------- f : pandas.DataFrame - Dataframe containing the column ``c``. - c : str - Name of the column in the dataframe ``f``. - p : int - The period over which to calculate the rolling mean. + Dataframe with columns ``open`` and ``close``. Returns ------- - new_column : pandas.Series (float) + new_column : pandas.Series (bool) The array containing the new feature. References ---------- - *In statistics, a moving average (rolling average or running average) - is a calculation to analyze data points by creating series of averages - of different subsets of the full data set* [WIKI_MA]_. - - .. [WIKI_MA] https://en.wikipedia.org/wiki/Moving_average + *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]_. """ - new_column = f[c].rolling(p).mean() + new_column = f['open'] < f['close'].shift(1) return new_column # -# Function ema +# Function gapup # -def ema(f, c, p = 20): - r"""Calculate the mean on a rolling basis. +def gapup(f): + r"""Determine whether or not there has been a gap up. Parameters ---------- f : pandas.DataFrame - Dataframe containing the column ``c``. - c : str - Name of the column in the dataframe ``f``. - p : int - The period over which to calculate the rolling mean. + Dataframe with columns ``open`` and ``close``. Returns ------- - new_column : pandas.Series (float) + new_column : pandas.Series (bool) The array containing the new feature. References ---------- - *An exponential moving average (EMA) is a type of moving average - that is similar to a simple moving average, except that more weight - is given to the latest data* [IP_EMA]_. - - .. [IP_EMA] http://www.investopedia.com/terms/e/ema.asp + *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]_. """ - new_column = pd.ewma(f[c], span=p) + new_column = f['open'] > f['close'].shift(1) return new_column # -# Function maratio +# Function gtval # -def maratio(f, c, p1 = 1, p2 = 10): - r"""Calculate the ratio of two moving averages. +def gtval(f, c1, c2): + r"""Determine whether or not the first column of a dataframe + is greater than the second. Parameters ---------- f : pandas.DataFrame - Dataframe containing the column ``c``. - c : str - Name of the column in the dataframe ``f``. - p1 : int - The period of the first moving average. - p2 : int - The period of the second moving average. + Dataframe containing the two columns ``c1`` and ``c2``. + c1 : str + Name of the first column in the dataframe ``f``. + c2 : str + Name of the second column in the dataframe ``f``. Returns ------- - new_column : pandas.Series (float) + new_column : pandas.Series (bool) The array containing the new feature. """ - new_column = ma(f, c, p1) / ma(f, c, p2) + new_column = f[c1] > f[c2] return new_column # -# Function net +# Function gtval0 # -def net(f, c='close', o = 1): - r"""Calculate the net change of a given column. +def gtval0(f, c1, c2): + r"""For positive values in the first column of the dataframe + that are greater than the second column, get the value in + the first column, otherwise return zero. Parameters ---------- f : pandas.DataFrame - Dataframe containing the column ``c``. - c : str - Name of the column in the dataframe ``f``. - o : int, optional - Offset value for shifting the series. + Dataframe containing the two columns ``c1`` and ``c2``. + c1 : str + Name of the first column in the dataframe ``f``. + c2 : str + Name of the second column in the dataframe ``f``. Returns ------- - new_column : pandas.Series (float) - The array containing the new feature. - - References - ---------- - *Net change is the difference between the closing price of a security - on the day's trading and the previous day's closing price. Net change - can be positive or negative and is quoted in terms of dollars* [IP_NET]_. - - .. [IP_NET] http://www.investopedia.com/terms/n/netchange.asp + new_val : float + A positive value or zero. """ - new_column = f[c] - f[c].shift(o) - return new_column + if f[c1] > f[c2] and f[c1] > 0: + new_val = f[c1] + else: + new_val = 0 + return new_val # -# Function gap +# Function higher # -def gap(f): - r"""Calculate the gap percentage between the current open and - the previous close. +def higher(f, c, o = 1): + r"""Determine whether or not a series value is higher than + the value ``o`` periods back. Parameters ---------- f : pandas.DataFrame - Dataframe with columns ``open`` and ``close``. + Dataframe containing the column ``c``. + c : str + Name of the column in the dataframe ``f``. + o : int, optional + Offset value for shifting the series. Returns ------- - new_column : pandas.Series (float) + new_column : pandas.Series (bool) 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) + new_column = f[c] > f[c].shift(o) return new_column # -# Function gapdown +# Function highest # -def gapdown(f): - r"""Determine whether or not there has been a gap down. +def highest(f, c, p = 20): + r"""Calculate the highest value on a rolling basis. Parameters ---------- f : pandas.DataFrame - Dataframe with columns ``open`` and ``close``. + Dataframe containing the column ``c``. + c : str + Name of the column in the dataframe ``f``. + p : int + The period over which to calculate the rolling maximum. Returns ------- - new_column : pandas.Series (bool) + 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]_. - """ - new_column = f['open'] < f['close'].shift(1) + new_column = f[c].rolling(p).max() return new_column # -# Function gapup +# Function hlrange # -def gapup(f): - r"""Determine whether or not there has been a gap up. +def hlrange(f, p = 1): + r"""Calculate the Range, the difference between High and Low. Parameters ---------- f : pandas.DataFrame - Dataframe with columns ``open`` and ``close``. + Dataframe with columns ``high`` and ``low``. + p : int + The period over which the range is calculated. Returns ------- - new_column : pandas.Series (bool) + 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]_. - """ - new_column = f['open'] > f['close'].shift(1) + new_column = highest(f, 'high', p) - lowest(f, 'low', p) return new_column # -# Function gapbadown +# Function lower # -def gapbadown(f): - r"""Determine whether or not there has been a breakaway gap down. +def lower(f, c, o = 1): + r"""Determine whether or not a series value is lower than + the value ``o`` periods back. Parameters ---------- f : pandas.DataFrame - Dataframe with columns ``open`` and ``low``. + Dataframe containing the column ``c``. + c : str + Name of the column in the dataframe ``f``. + o : int, optional + Offset value for shifting the series. Returns ------- new_column : pandas.Series (bool) The array containing the new feature. - References - ---------- - *A breakaway gap represents a gap in the movement of a stock price - supported by levels of high volume* [IP_BAGAP]_. - - .. [IP_BAGAP] http://www.investopedia.com/terms/b/breakawaygap.asp - """ - new_column = f['open'] < f['low'].shift(1) + new_column = f[c] < f[c].shift(o) return new_column # -# Function gapbaup +# Function lowest # -def gapbaup(f): - r"""Determine whether or not there has been a breakaway gap up. +def lowest(f, c, p = 20): + r"""Calculate the lowest value on a rolling basis. Parameters ---------- f : pandas.DataFrame - Dataframe with columns ``open`` and ``high``. + Dataframe containing the column ``c``. + c : str + Name of the column in the dataframe ``f``. + p : int + The period over which to calculate the rolling minimum. Returns ------- - new_column : pandas.Series (bool) + new_column : pandas.Series (float) The array containing the new feature. - References - ---------- - *A breakaway gap represents a gap in the movement of a stock price - supported by levels of high volume* [IP_BAGAP]_. - """ - new_column = f['open'] > f['high'].shift(1) - return new_column + return f[c].rolling(p).min() # -# Function truehigh +# Function ma # -def truehigh(f): - r"""Calculate the *True High* value. +def ma(f, c, p = 20): + r"""Calculate the mean on a rolling basis. Parameters ---------- f : pandas.DataFrame - Dataframe with columns ``high`` and ``low``. + Dataframe containing the column ``c``. + c : str + Name of the column in the dataframe ``f``. + p : int + The period over which to calculate the rolling mean. Returns ------- @@ -1209,94 +904,100 @@ def truehigh(f): References ---------- - *Today's high, or the previous close, whichever is higher* [TS_TR]_. + *In statistics, a moving average (rolling average or running average) + is a calculation to analyze data points by creating series of averages + of different subsets of the full data set* [WIKI_MA]_. - .. [TS_TR] http://help.tradestation.com/09_01/tradestationhelp/charting_definitions/true_range.htm + .. [WIKI_MA] https://en.wikipedia.org/wiki/Moving_average """ - c1 = 'low[1]' - vexec(f, c1) - c2 = 'high' - new_column = f.apply(c2max, axis=1, args=[c1, c2]) + new_column = f[c].rolling(p).mean() return new_column # -# Function truelow +# Function maratio # -def truelow(f): - r"""Calculate the *True Low* value. +def maratio(f, c, p1 = 1, p2 = 10): + r"""Calculate the ratio of two moving averages. Parameters ---------- f : pandas.DataFrame - Dataframe with columns ``high`` and ``low``. + Dataframe containing the column ``c``. + c : str + Name of the column in the dataframe ``f``. + p1 : int + The period of the first moving average. + p2 : int + The period of the second moving average. 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]) + new_column = ma(f, c, p1) / ma(f, c, p2) return new_column # -# Function truerange +# Function mval # - -def truerange(f): - r"""Calculate the *True Range* value. + +def mval(f, c): + r"""Get the negative value, otherwise zero. Parameters ---------- f : pandas.DataFrame - Dataframe with columns ``high`` and ``low``. + Dataframe containing the column ``c``. + c : str + Name of the column in the dataframe ``f``. Returns ------- - new_column : pandas.Series (float) - The array containing the new feature. - - References - ---------- - *True High - True Low* [TS_TR]_. + new_val : float + Negative value or zero. """ - new_column = truehigh(f) - truelow(f) - return new_column + new_val = -f[c] if f[c] < 0 else 0 + return new_val # -# Function hlrange +# Function net # -def hlrange(f, p = 1): - r"""Calculate the Range, the difference between High and Low. +def net(f, c='close', o = 1): + r"""Calculate the net change of a given column. Parameters ---------- f : pandas.DataFrame - Dataframe with columns ``high`` and ``low``. - p : int - The period over which the range is calculated. + Dataframe containing the column ``c``. + c : str + Name of the column in the dataframe ``f``. + o : int, optional + Offset value for shifting the series. Returns ------- new_column : pandas.Series (float) The array containing the new feature. + References + ---------- + *Net change is the difference between the closing price of a security + on the day's trading and the previous day's closing price. Net change + can be positive or negative and is quoted in terms of dollars* [IP_NET]_. + + .. [IP_NET] http://www.investopedia.com/terms/n/netchange.asp + """ - new_column = highest(f, 'high', p) - lowest(f, 'low', p) + new_column = f[c] - f[c].shift(o) return new_column @@ -1336,32 +1037,20 @@ def netreturn(f, c, o = 1): # -# Function rindex +# Function pchange1 # - -def rindex(f, ci, ch, cl, p = 1): - r"""Calculate the *range index* spanning a given period ``p``. - - The **range index** is a number between 0 and 100 that - relates the value of the index column ``ci`` to the - high column ``ch`` and the low column ``cl``. For example, - if the low value of the range is 10 and the high value - is 20, then the range index for a value of 15 would be 50%. - The range index for 18 would be 80%. + +def pchange1(f, c, o = 1): + r"""Calculate the percentage change within the same variable. Parameters ---------- f : pandas.DataFrame - Dataframe containing the columns ``ci``, ``ch``, and ``cl``. - ci : str - Name of the index column in the dataframe ``f``. - ch : str - Name of the high column in the dataframe ``f``. - cl : str - Name of the low column in the dataframe ``f``. - p : int - The period over which the range index of column ``ci`` - is calculated. + Dataframe containing the column ``c``. + c : str + Name of the column in the dataframe ``f``. + o : int + Offset to the previous value. Returns ------- @@ -1369,37 +1058,34 @@ def rindex(f, ci, ch, cl, p = 1): The array containing the new feature. """ - o = p-1 if f[ci].name == 'open' else 0 - hh = highest(f, ch, p) - ll = lowest(f, cl, p) - fn = f[ci].shift(o) - ll - fd = hh - ll - new_column = 100 * fn / fd + new_column = f[c] / f[c].shift(o) - 1.0 return new_column # -# Function mval +# Function pchange2 # - -def mval(f, c): - r"""Get the negative value, otherwise zero. + +def pchange2(f, c1, c2): + r"""Calculate the percentage change between two variables. Parameters ---------- f : pandas.DataFrame - Dataframe containing the column ``c``. - c : str - Name of the column in the dataframe ``f``. + Dataframe containing the two columns ``c1`` and ``c2``. + c1 : str + Name of the first column in the dataframe ``f``. + c2 : str + Name of the second column in the dataframe ``f``. Returns ------- - new_val : float - Negative value or zero. + new_column : pandas.Series (float) + The array containing the new feature. """ - new_val = -f[c] if f[c] < 0 else 0 - return new_val + new_column = f[c1] / f[c2] - 1.0 + return new_column # @@ -1427,42 +1113,32 @@ def pval(f, c): # -# Function dpc +# Function rindex # -def dpc(f, c): - r"""Get the negative values, with positive values zeroed. - - Parameters - ---------- - f : pandas.DataFrame - Dataframe with column ``c``. - c : str - Name of the column. - - Returns - ------- - new_column : pandas.Series (float) - The array containing the new feature. - - """ - new_column = f.apply(mval, axis=1, args=[c]) - return new_column - - -# -# Function upc -# +def rindex(f, ci, ch, cl, p = 1): + r"""Calculate the *range index* spanning a given period ``p``. -def upc(f, c): - r"""Get the positive values, with negative values zeroed. + The **range index** is a number between 0 and 100 that + relates the value of the index column ``ci`` to the + high column ``ch`` and the low column ``cl``. For example, + if the low value of the range is 10 and the high value + is 20, then the range index for a value of 15 would be 50%. + The range index for 18 would be 80%. Parameters ---------- f : pandas.DataFrame - Dataframe with column ``c``. - c : str - Name of the column. + Dataframe containing the columns ``ci``, ``ch``, and ``cl``. + ci : str + Name of the index column in the dataframe ``f``. + ch : str + Name of the high column in the dataframe ``f``. + cl : str + Name of the low column in the dataframe ``f``. + p : int + The period over which the range index of column ``ci`` + is calculated. Returns ------- @@ -1470,7 +1146,12 @@ def upc(f, c): The array containing the new feature. """ - new_column = f.apply(pval, axis=1, args=[c]) + o = p-1 if f[ci].name == 'open' else 0 + hh = highest(f, ch, p) + ll = lowest(f, cl, p) + fn = f[ci].shift(o) - ll + fd = hh - ll + new_column = 100 * fn / fd return new_column @@ -1514,146 +1195,248 @@ def rsi(f, c, p = 14): # -# Function gtval +# Function rtotal # -def gtval(f, c1, c2): - r"""Determine whether or not the first column of a dataframe - is greater than the second. +def rtotal(vec): + r"""Calculate the running total. Parameters ---------- - f : pandas.DataFrame - Dataframe containing the two columns ``c1`` and ``c2``. - c1 : str - Name of the first column in the dataframe ``f``. - c2 : str - Name of the second column in the dataframe ``f``. + vec : pandas.Series + The input array for calculating the running total. Returns ------- - new_column : pandas.Series (bool) - The array containing the new feature. + running_total : int + The final running total. + + Example + ------- + + >>> vec.rolling(window=20).apply(rtotal) """ - new_column = f[c1] > f[c2] - return new_column + tcount = np.count_nonzero(vec) + fcount = len(vec) - tcount + running_total = tcount - fcount + return running_total # -# Function gtval0 +# Function runs # -def gtval0(f, c1, c2): - r"""For positive values in the first column of the dataframe - that are greater than the second column, get the value in - the first column, otherwise return zero. +def runs(vec): + r"""Calculate the total number of runs. Parameters ---------- - f : pandas.DataFrame - Dataframe containing the two columns ``c1`` and ``c2``. - c1 : str - Name of the first column in the dataframe ``f``. - c2 : str - Name of the second column in the dataframe ``f``. + vec : pandas.Series + The input array for calculating the number of runs. Returns ------- - new_val : float - A positive value or zero. + runs_value : int + The total number of runs. + + Example + ------- + + >>> vec.rolling(window=20).apply(runs) """ - if f[c1] > f[c2] and f[c1] > 0: - new_val = f[c1] - else: - new_val = 0 - return new_val + runs_value = len(list(itertools.groupby(vec))) + return runs_value # -# Function dmplus +# Function runs_test # -def dmplus(f): - r"""Calculate the Plus Directional Movement (+DM). +def runs_test(f, c, wfuncs, window): + r"""Perform a runs test on binary series. Parameters ---------- f : pandas.DataFrame - Dataframe with columns ``high`` and ``low``. + Dataframe containing the column ``c``. + c : str + Name of the column in the dataframe ``f``. + wfuncs : list + The set of runs test functions to apply to the column: + + ``'all'``: + Run all of the functions below. + ``'rtotal'``: + The running total over the ``window`` period. + ``'runs'``: + Total number of runs in ``window``. + ``'streak'``: + The length of the latest streak. + ``'zscore'``: + The Z-Score over the ``window`` period. + window : int + The rolling period. Returns ------- - new_column : pandas.Series (float) - The array containing the new feature. + new_features : pandas.DataFrame + The dataframe containing the runs test features. References ---------- - *Directional movement is positive (plus) when the current high minus - the prior high is greater than the prior low minus the current low. - This so-called Plus Directional Movement (+DM) then equals the current - high minus the prior high, provided it is positive. A negative value - would simply be entered as zero* [SC_ADX]_. + For more information about runs tests for detecting non-randomness, + refer to [RUNS]_. - .. [SC_ADX] http://stockcharts.com/school/doku.php?id=chart_school:technical_indicators:average_directional_index_adx + .. [RUNS] http://www.itl.nist.gov/div898/handbook/eda/section3/eda35d.htm """ - c1 = 'upmove' - f[c1] = net(f, 'high') - c2 = 'downmove' - f[c2] = -net(f, 'low') - new_column = f.apply(gtval0, axis=1, args=[c1, c2]) - return new_column + + fc = f[c] + all_funcs = {'runs' : runs, + 'streak' : streak, + 'rtotal' : rtotal, + 'zscore' : zscore} + # use all functions + if 'all' in wfuncs: + wfuncs = list(all_funcs.keys()) + # apply each of the runs functions + new_features = pd.DataFrame() + for w in wfuncs: + if w in all_funcs: + new_feature = fc.rolling(window=window).apply(all_funcs[w]) + new_feature.fillna(0, inplace=True) + new_column_name = PSEP.join([c, w]) + new_feature = new_feature.rename(new_column_name) + frames = [new_features, new_feature] + new_features = pd.concat(frames, axis=1) + else: + logger.info("Runs Function %s not found", w) + return new_features # -# Function dminus +# Function split_to_letters # -def dminus(f): - r"""Calculate the Minus Directional Movement (-DM). +def split_to_letters(f, c): + r"""Separate text into distinct characters. Parameters ---------- f : pandas.DataFrame - Dataframe with columns ``high`` and ``low``. + Dataframe containing the column ``c``. + c : str + Name of the text column in the dataframe ``f``. Returns ------- - new_column : pandas.Series (float) + new_feature : pandas.Series The array containing the new feature. - References + Example + ------- + The value 'abc' becomes 'a b c'. + + """ + fc = f[c] + new_feature = None + dtype = fc.dtypes + if dtype == 'object': + fc.fillna(NULLTEXT, inplace=True) + maxlen = fc.str.len().max() + if maxlen > 1: + new_feature = fc.apply(lambda x: BSEP.join(list(x))) + return new_feature + + +# +# Function streak +# + +def streak(vec): + r"""Determine the length of the latest streak. + + Parameters ---------- - *Directional movement is negative (minus) when the prior low minus - the current low is greater than the current high minus the prior high. - This so-called Minus Directional Movement (-DM) equals the prior low - minus the current low, provided it is positive. A negative value - would simply be entered as zero* [SC_ADX]_. + vec : pandas.Series + The input array for calculating the latest streak. + + Returns + ------- + latest_streak : int + The length of the latest streak. + + Example + ------- + + >>> vec.rolling(window=20).apply(streak) """ - c1 = 'downmove' - f[c1] = -net(f, 'low') - c2 = 'upmove' - f[c2] = net(f, 'high') - new_column = f.apply(gtval0, axis=1, args=[c1, c2]) - return new_column + latest_streak = [len(list(g)) for k, g in itertools.groupby(vec)][-1] + return latest_streak # -# Function diplus +# Function texplode # -def diplus(f, p = 14): - r"""Calculate the Plus Directional Indicator (+DI). +def texplode(f, c): + r"""Get dummy values for a text column. + + Parameters + ---------- + f : pandas.DataFrame + Dataframe containing the column ``c``. + c : str + Name of the text column in the dataframe ``f``. + + Returns + ------- + dummies : pandas.DataFrame + The dataframe containing the dummy variables. + + Example + ------- + + This function is useful for columns that appear to + have separate character codes but are consolidated + into a single column. Here, the column ``c`` is + transformed into five dummy variables. + + === === === === === === + c 0_a 1_x 1_b 2_x 2_z + === === === === === === + abz 1 0 1 0 1 + abz 1 0 1 0 1 + axx 1 1 0 1 0 + abz 1 0 1 0 1 + axz 1 1 0 0 1 + === === === === === === + + """ + fc = f[c] + maxlen = fc.str.len().max() + fc.fillna(maxlen * BSEP, inplace=True) + fpad = str().join(['{:', BSEP, '>', str(maxlen), '}']) + fcpad = fc.apply(fpad.format) + fcex = fcpad.apply(lambda x: pd.Series(list(x))) + dummies = pd.get_dummies(fcex) + return dummies + + +# +# Function truehigh +# + +def truehigh(f): + r"""Calculate the *True High* value. Parameters ---------- f : pandas.DataFrame Dataframe with columns ``high`` and ``low``. - p : int - The period over which to calculate the +DI. Returns ------- @@ -1662,36 +1445,29 @@ def diplus(f, p = 14): References ---------- - *A component of the average directional index (ADX) that is used to - measure the presence of an uptrend. When the +DI is sloping upward, - it is a signal that the uptrend is getting stronger* [IP_PDI]_. + *Today's high, or the previous close, whichever is higher* [TS_TR]_. - .. [IP_PDI] http://www.investopedia.com/terms/p/positivedirectionalindicator.asp + .. [TS_TR] http://help.tradestation.com/09_01/tradestationhelp/charting_definitions/true_range.htm """ - tr = 'truerange' - vexec(f, tr) - atr = USEP.join(['atr', str(p)]) - vexec(f, atr) - dmp = 'dmplus' - vexec(f, dmp) - new_column = 100 * f[dmp].ewm(span=p).mean() / f[atr] + c1 = 'low[1]' + vexec(f, c1) + c2 = 'high' + new_column = f.apply(c2max, axis=1, args=[c1, c2]) return new_column # -# Function diminus +# Function truelow # -def diminus(f, p = 14): - r"""Calculate the Minus Directional Indicator (-DI). +def truelow(f): + r"""Calculate the *True Low* value. Parameters ---------- f : pandas.DataFrame Dataframe with columns ``high`` and ``low``. - p : int - The period over which to calculate the -DI. Returns ------- @@ -1700,38 +1476,27 @@ def diminus(f, p = 14): 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 + *Today's low, or the previous close, whichever is lower* [TS_TR]_. """ - 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] + c1 = 'high[1]' + vexec(f, c1) + c2 = 'low' + new_column = f.apply(c2min, axis=1, args=[c1, c2]) return new_column # -# Function adx +# Function truerange # -def adx(f, p = 14): - r"""Calculate the Average Directional Index (ADX). +def truerange(f): + r"""Calculate the *True Range* value. 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. + Dataframe with columns ``high`` and ``low``. Returns ------- @@ -1740,33 +1505,19 @@ def adx(f, p = 14): 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 + *True High - True Low* [TS_TR]_. """ - 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 + new_column = truehigh(f) - truelow(f) return new_column # -# Function abovema +# Function up # -def abovema(f, c, p = 50): - r"""Determine those values of the dataframe that are above the - moving average. +def up(f, c): + r"""Find the positive values in the series. Parameters ---------- @@ -1774,8 +1525,6 @@ def abovema(f, c, p = 50): Dataframe containing the column ``c``. c : str Name of the column in the dataframe ``f``. - p : int - The period of the moving average. Returns ------- @@ -1783,34 +1532,31 @@ def abovema(f, c, p = 50): The array containing the new feature. """ - new_column = f[c] > ma(f, c, p) + new_column = f[c] > 0 return new_column # -# Function belowma +# Function upc # -def belowma(f, c, p = 50): - r"""Determine those values of the dataframe that are below the - moving average. +def upc(f, c): + r"""Get the positive values, with negative values zeroed. Parameters ---------- f : pandas.DataFrame - Dataframe containing the column ``c``. + Dataframe with column ``c``. c : str - Name of the column in the dataframe ``f``. - p : int - The period of the moving average. + Name of the column. Returns ------- - new_column : pandas.Series (bool) + new_column : pandas.Series (float) The array containing the new feature. """ - new_column = f[c] < ma(f, c, p) + new_column = f.apply(pval, axis=1, args=[c]) return new_column @@ -1896,3 +1642,47 @@ def xmaup(f, c='close', pfast = 20, pslow = 50): lma_prev = lma.shift(1) new_column = (sma > lma) & (sma_prev < lma_prev) return new_column + + +# +# Function zscore +# + +def zscore(vec): + r"""Calculate the Z-Score. + + Parameters + ---------- + vec : pandas.Series + The input array for calculating the Z-Score. + + Returns + ------- + zscore : float + The value of the Z-Score. + + References + ---------- + To calculate the Z-Score, you can find more information here [ZSCORE]_. + + .. [ZSCORE] https://en.wikipedia.org/wiki/Standard_score + + Example + ------- + + >>> vec.rolling(window=20).apply(zscore) + + """ + n1 = np.count_nonzero(vec) + n2 = len(vec) - n1 + fac1 = float(2 * n1 * n2) + fac2 = float(n1 + n2) + rbar = fac1 / fac2 + 1 + sr2num = fac1 * (fac1 - n1 - n2) + sr2den = math.pow(fac2, 2) * (fac2 - 1) + sr = math.sqrt(sr2num / sr2den) + if sr2den and sr: + zscore = (runs(vec) - rbar) / sr + else: + zscore = 0 + return zscore diff --git a/alphapy/variables.py b/alphapy/variables.py new file mode 100644 index 0000000..8477647 --- /dev/null +++ b/alphapy/variables.py @@ -0,0 +1,612 @@ +################################################################################ +# +# Package : AlphaPy +# Module : variables +# Created : July 11, 2013 +# +# Copyright 2020 ScottFree Analytics LLC +# Mark Conway & Robert D. Scott II +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# +################################################################################ + + +# +# Variables +# --------- +# +# Numeric substitution is allowed for any number in the expression. +# Offsets are allowed in event expressions but cannot be substituted. +# +# Examples +# -------- +# +# Variable('rrunder', 'rr_3_20 <= 0.9') +# +# 'rrunder_2_10_0.7' +# 'rrunder_2_10_0.9' +# 'xmaup_20_50_20_200' +# 'xmaup_10_50_20_50' +# + + +# +# Imports +# + +from alphapy.alias import get_alias +from alphapy.frame import Frame +from alphapy.frame import frame_name +from alphapy.globals import BSEP, LOFF, ROFF, USEP +from alphapy.utilities import valid_name + +import builtins +from collections import OrderedDict +from importlib import import_module +import logging +import numpy as np +import pandas as pd +import parser +import re +import sys + + +# +# Initialize logger +# + +logger = logging.getLogger(__name__) + + +# +# Class Variable +# + +class Variable(object): + """Create a new variable as a key-value pair. All variables are stored + in ``Variable.variables``. Duplicate keys or values are not allowed, + unless the ``replace`` parameter is ``True``. + + Parameters + ---------- + name : str + Variable key. + expr : str + Variable value. + replace : bool, optional + Replace the current key-value pair if it already exists. + + Attributes + ---------- + variables : dict + Class variable for storing all known variables + + Examples + -------- + + >>> Variable('rrunder', 'rr_3_20 <= 0.9') + >>> Variable('hc', 'higher_close') + + """ + + # class variable to track all variables + + variables = {} + + # function __new__ + + def __new__(cls, + name, + expr, + replace = False): + # code + efound = expr in [Variable.variables[key].expr for key in Variable.variables] + if efound: + key = [key for key in Variable.variables if expr in Variable.variables[key].expr] + logger.info("Expression '%s' already exists for key %s", expr, key) + return + else: + if replace or not name in Variable.variables: + if not valid_name(name): + logger.info("Invalid variable key: %s", name) + return + try: + result = parser.expr(expr) + except: + logger.info("Invalid expression: %s", expr) + return + return super(Variable, cls).__new__(cls) + else: + logger.info("Key %s already exists", name) + + # function __init__ + + def __init__(self, + name, + expr, + replace = False): + # code + self.name = name; + self.expr = expr; + # add key with expression + Variable.variables[name] = self + + # function __str__ + + def __str__(self): + return self.expr + + +# +# Function vparse +# + +def vparse(vname): + r"""Parse a variable name into its respective components. + + Parameters + ---------- + vname : str + The name of the variable. + + Returns + ------- + vxlag : str + Variable name without the ``lag`` component. + root : str + The base variable name without the parameters. + plist : list + The parameter list. + lag : int + The offset starting with the current value [0] + and counting back, e.g., an offset [1] means the + previous value of the variable. + + Notes + ----- + + **AlphaPy** makes feature creation easy. The syntax + of a variable name maps to a function call: + + xma_20_50 => xma(20, 50) + + Examples + -------- + + >>> vparse('xma_20_50[1]') + # ('xma_20_50', 'xma', ['20', '50'], 1) + + """ + + # split along lag first + lsplit = vname.split(LOFF) + vxlag = lsplit[0] + # if necessary, substitute any alias + root = vxlag.split(USEP)[0] + alias = get_alias(root) + if alias: + vxlag = vxlag.replace(root, alias) + vsplit = vxlag.split(USEP) + root = vsplit[0] + plist = vsplit[1:] + # extract lag + lag = 0 + if len(lsplit) > 1: + # lag is present + slag = lsplit[1].replace(ROFF, '') + if len(slag) > 0: + lpat = r'(^-?[0-9]+$)' + lre = re.compile(lpat) + if lre.match(slag): + lag = int(slag) + # return all components + return vxlag, root, plist, lag + + +# +# Function allvars +# + +def allvars(expr): + r"""Get the list of valid names in the expression. + + Parameters + ---------- + expr : str + A valid expression conforming to the Variable Definition Language. + + Returns + ------- + vlist : list + List of valid variable names. + + """ + regex = re.compile('\w+') + items = regex.findall(expr) + vlist = [] + for item in items: + if valid_name(item): + vlist.append(item) + return vlist + + +# +# Function vtree +# + +def vtree(vname): + r"""Get all of the antecedent variables. + + Before applying a variable to a dataframe, we have to recursively + get all of the child variables, beginning with the starting variable's + expression. Then, we have to extract the variables from all the + subsequent expressions. This process continues until all antecedent + variables are obtained. + + Parameters + ---------- + vname : str + A valid variable stored in ``Variable.variables``. + + Returns + ------- + all_variables : list + The variables that need to be applied before ``vname``. + + Other Parameters + ---------------- + Variable.variables : dict + Global dictionary of variables + + """ + allv = [] + def vwalk(allv, vname): + vxlag, root, plist, lag = vparse(vname) + if root in Variable.variables: + root_expr = Variable.variables[root].expr + expr = vsub(vname, root_expr) + av = allvars(expr) + for v in av: + vwalk(allv, v) + else: + for p in plist: + if valid_name(p): + vwalk(allv, p) + allv.append(vname) + return allv + allv = vwalk(allv, vname) + all_variables = list(OrderedDict.fromkeys(allv)) + return all_variables + + +# +# Function vsub +# + +def vsub(v, expr): + r"""Substitute the variable parameters into the expression. + + This function performs the parameter substitution when + applying features to a dataframe. It is a mechanism for + the user to override the default values in any given + expression when defining a feature, instead of having + to programmatically call a function with new values. + + Parameters + ---------- + v : str + Variable name. + expr : str + The expression for substitution. + + Returns + ------- + newexpr + The expression with the new, substituted values. + + """ + # numbers pattern + npat = '[-+]?[0-9]*\.?[0-9]+' + nreg = re.compile(npat) + # find all number locations in variable name + vnums = nreg.findall(v) + viter = nreg.finditer(v) + vlocs = [] + for match in viter: + vlocs.append(match.span()) + # find all number locations in expression + # find all non-number locations as well + elen = len(expr) + enums = nreg.findall(expr) + eiter = nreg.finditer(expr) + elocs = [] + enlocs = [] + index = 0 + for match in eiter: + eloc = match.span() + elocs.append(eloc) + enlocs.append((index, eloc[0])) + index = eloc[1] + # build new expression + newexpr = str() + for i, enloc in enumerate(enlocs): + if i < len(vlocs): + newexpr += expr[enloc[0]:enloc[1]] + v[vlocs[i][0]:vlocs[i][1]] + else: + newexpr += expr[enloc[0]:enloc[1]] + expr[elocs[i][0]:elocs[i][1]] + if elocs: + estart = elocs[len(elocs)-1][1] + else: + estart = 0 + newexpr += expr[estart:elen] + return newexpr + + +# +# Function vexec +# + +def vexec(f, v, vfuncs=None): + r"""Add a variable to the given dataframe. + + This is the core function for adding a variable to a dataframe. + The default variable functions are already defined locally + in ``alphapy.transforms``; however, you may want to define your + own variable functions. If so, then the ``vfuncs`` parameter + will contain the list of modules and functions to be imported + and applied by the ``vexec`` function. + + To write your own variable function, your function must have + a pandas *DataFrame* as an input parameter and must return + a pandas *DataFrame* with the new variable(s). + + Parameters + ---------- + f : pandas.DataFrame + Dataframe to contain the new variable. + v : str + Variable to add to the dataframe. + vfuncs : dict, optional + Dictionary of external modules and functions. + + Returns + ------- + f : pandas.DataFrame + Dataframe with the new variable. + + Other Parameters + ---------------- + Variable.variables : dict + Global dictionary of variables + + """ + vxlag, root, plist, lag = vparse(v) + logger.debug("vexec : %s", v) + logger.debug("vxlag : %s", vxlag) + logger.debug("root : %s", root) + logger.debug("plist : %s", plist) + logger.debug("lag : %s", lag) + if vxlag not in f.columns: + if root in Variable.variables: + logger.debug("Found variable %s: ", root) + vroot = Variable.variables[root] + expr = vroot.expr + expr_new = vsub(vxlag, expr) + estr = "%s" % expr_new + logger.debug("Expression: %s", estr) + # pandas eval + f[vxlag] = f.eval(estr) + else: + logger.debug("Did not find variable: %s", root) + # Must be a function call + func_name = root + # Convert the parameter list and prepend the data frame + newlist = [] + for p in plist: + try: + newlist.append(int(p)) + except: + try: + newlist.append(float(p)) + except: + newlist.append(p) + newlist.insert(0, f) + # Find the module and function + module = None + if vfuncs: + for m in vfuncs: + funcs = vfuncs[m] + if func_name in funcs: + module = m + break + # If the module was found, import the external transform function, + # else search the local namespace and AlphaPy. + if module: + ext_module = import_module(module) + func = getattr(ext_module, func_name) + else: + modname = globals()['__name__'] + module = sys.modules[modname] + if func_name in dir(module): + func = getattr(module, func_name) + else: + try: + ap_module = import_module('alphapy.transforms') + func = getattr(ap_module, func_name) + except: + func = None + if func: + # Create the variable by calling the function + f[v] = func(*newlist) + elif func_name not in dir(builtins): + module_error = "*** Could not find module to execute function {} ***".format(func_name) + logger.error(module_error) + sys.exit(module_error) + # if necessary, add the lagged variable + if lag > 0 and vxlag in f.columns: + f[v] = f[vxlag].shift(lag) + # output frame + return f + + +# +# Function vapply +# + +def vapply(group, vname, vfuncs=None): + r"""Apply a variable to multiple dataframes. + + Parameters + ---------- + group : alphapy.Group + The input group. + vname : str + The variable to apply to the ``group``. + vfuncs : dict, optional + Dictionary of external modules and functions. + + Returns + ------- + None : None + + Other Parameters + ---------------- + Frame.frames : dict + Global dictionary of dataframes + + See Also + -------- + vunapply + + """ + # get all frame names to apply variables + gnames = [item.lower() for item in group.members] + # get all the precedent variables + allv = vtree(vname) + # apply the variables to each frame + for g in gnames: + fname = frame_name(g, group.space) + if fname in Frame.frames: + f = Frame.frames[fname].df + if not f.empty: + for v in allv: + logger.debug("Applying variable %s to %s", v, g) + f = vexec(f, v, vfuncs) + else: + logger.debug("Frame for %s is empty", g) + else: + logger.debug("Frame not found: %s", fname) + + +# +# Function vmapply +# + +def vmapply(group, vs, vfuncs=None): + r"""Apply multiple variables to multiple dataframes. + + Parameters + ---------- + group : alphapy.Group + The input group. + vs : list + The list of variables to apply to the ``group``. + vfuncs : dict, optional + Dictionary of external modules and functions. + + Returns + ------- + None : None + + See Also + -------- + vmunapply + + """ + for v in vs: + logger.info("Applying variable: %s", v) + vapply(group, v, vfuncs) + + +# +# Function vunapply +# + +def vunapply(group, vname): + r"""Remove a variable from multiple dataframes. + + Parameters + ---------- + group : alphapy.Group + The input group. + vname : str + The variable to remove from the ``group``. + + Returns + ------- + None : None + + Other Parameters + ---------------- + Frame.frames : dict + Global dictionary of dataframes + + See Also + -------- + vapply + + """ + # get all frame names to apply variables + gnames = [item.lower() for item in group.all_members()] + # apply the variables to each frame + for g in gnames: + fname = frame_name(g, group.space) + if fname in Frame.frames: + f = Frame.frames[fname].df + logger.info("Unapplying variable %s from %s", vname, g) + if vname not in f.columns: + logger.info("Variable %s not in %s frame", vname, g) + else: + estr = "Frame.frames['%s'].df = f.df.drop('%s', axis=1)" \ + % (fname, vname) + exec(estr) + else: + logger.info("Frame not found: %s", fname) + + +# +# Function vmunapply +# + +def vmunapply(group, vs): + r"""Remove a list of variables from multiple dataframes. + + Parameters + ---------- + group : alphapy.Group + The input group. + vs : list + The list of variables to remove from the ``group``. + + Returns + ------- + None : None + + See Also + -------- + vmapply + + """ + for v in vs: + vunapply(group, v) From b311a7128bd0534c02b3d86c61c8ae6218dbd85e Mon Sep 17 00:00:00 2001 From: Mark Conway Date: Sun, 15 Mar 2020 10:25:44 -0400 Subject: [PATCH 076/129] version 2.4.2 --- setup.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/setup.py b/setup.py index fba1555..568eff7 100644 --- a/setup.py +++ b/setup.py @@ -10,7 +10,7 @@ MAINTAINER_EMAIL = 'scottfree.analytics@scottfreellc.com' URL = "https://github.com/ScottFreeLLC/AlphaPy" LICENSE = "Apache License, Version 2" -VERSION = "2.4.1" +VERSION = "2.4.2" classifiers = ['Development Status :: 4 - Beta', 'Programming Language :: Python', From b774afd802153d5280ad79fba3675c1d60dc4f69 Mon Sep 17 00:00:00 2001 From: Mark Conway Date: Sun, 15 Mar 2020 10:26:41 -0400 Subject: [PATCH 077/129] version 2.4.2 --- docs/conf.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/conf.py b/docs/conf.py index a8783f0..719aa58 100644 --- a/docs/conf.py +++ b/docs/conf.py @@ -62,9 +62,9 @@ # built documents. # # The short X.Y version. -version = '2.4.1' +version = '2.4.2' # The full version, including alpha/beta/rc tags. -release = '2.4.1' +release = '2.4.2' # The language for content autogenerated by Sphinx. Refer to documentation # for a list of supported languages. From 69f3c33d414662ce0a280ac81fb8a2a8edd181a3 Mon Sep 17 00:00:00 2001 From: Mark Conway Date: Sun, 15 Mar 2020 17:41:11 -0400 Subject: [PATCH 078/129] update dependencies in environment.yml update dependencies in environment.yml --- .gitignore | 1 + environment.yml | 5 ++--- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/.gitignore b/.gitignore index 7d37039..6453caf 100644 --- a/.gitignore +++ b/.gitignore @@ -21,3 +21,4 @@ alphapy/examples/Trading System/.ipynb_checkpoints/A Trading System-checkpoint.i alphapy/.vscode/launch.json alphapy/.vscode/settings.json *.log +docs/.vscode/settings.json diff --git a/environment.yml b/environment.yml index 4ee2778..ef62905 100644 --- a/environment.yml +++ b/environment.yml @@ -9,13 +9,12 @@ dependencies: - keras>=2.2 - matplotlib>=3.0 - numpy>=1.17 -- pandas>=0.24 +- pandas>=1.0 - pyyaml>=5.0 -- scikit-learn>=0.21 +- scikit-learn>=0.22 - scipy>=1.1 - seaborn>=0.9 - tensorflow>=1.15 -- xgboost>=0.8 - pip: - arrow>=0.13 - category_encoders>=2.1 From de31c37d17a8b8d896399dfc587a4b4015d58737 Mon Sep 17 00:00:00 2001 From: Mark Conway Date: Sun, 15 Mar 2020 17:41:49 -0400 Subject: [PATCH 079/129] update docs modules update docs modules --- docs/source/alphapy.rst | 32 ++++++++++++++++++++++++-------- 1 file changed, 24 insertions(+), 8 deletions(-) diff --git a/docs/source/alphapy.rst b/docs/source/alphapy.rst index 058c8e1..1786679 100644 --- a/docs/source/alphapy.rst +++ b/docs/source/alphapy.rst @@ -28,6 +28,14 @@ alphapy.analysis module :undoc-members: :show-inheritance: +alphapy.calendrical module +-------------------------- + +.. automodule:: alphapy.calendrical + :members: + :undoc-members: + :show-inheritance: + alphapy.data module ------------------- @@ -84,14 +92,6 @@ alphapy.market_flow module :undoc-members: :show-inheritance: -alphapy.market_variables module -------------------------------- - -.. automodule:: alphapy.market_variables - :members: - :undoc-members: - :show-inheritance: - alphapy.model module -------------------- @@ -148,6 +148,14 @@ alphapy.system module :undoc-members: :show-inheritance: +alphapy.transforms module +------------------------- + +.. automodule:: alphapy.transforms + :members: + :undoc-members: + :show-inheritance: + alphapy.utilities module ------------------------ @@ -155,3 +163,11 @@ alphapy.utilities module :members: :undoc-members: :show-inheritance: + +alphapy.variables module +------------------------ + +.. automodule:: alphapy.variables + :members: + :undoc-members: + :show-inheritance: From e9df97ec16e6f4959d499802d5508ba9dc09edc3 Mon Sep 17 00:00:00 2001 From: Mark Conway Date: Sun, 15 Mar 2020 17:42:13 -0400 Subject: [PATCH 080/129] search path logging bug search path logging bug --- alphapy/model.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/alphapy/model.py b/alphapy/model.py index 9346a3b..d093aaf 100644 --- a/alphapy/model.py +++ b/alphapy/model.py @@ -579,7 +579,7 @@ def load_feature_map(model, directory): feature_map = joblib.load(file_name) model.feature_map = feature_map except: - logging.error("Could not find feature map in %s", search_path) + logging.error("Could not find feature map in %s", search_dir) # Return the model with the feature map return model From aacc3f012ee3ed698a00f81ad57c270ac61a5766 Mon Sep 17 00:00:00 2001 From: Mark Conway Date: Mon, 6 Apr 2020 12:44:29 -0400 Subject: [PATCH 081/129] Downloads Badge Downloads Badge --- README.rst | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/README.rst b/README.rst index 9371734..0434302 100644 --- a/README.rst +++ b/README.rst @@ -1,7 +1,7 @@ AlphaPy ======= -|badge_pypi| |badge_build| |badge_docs| +|badge_pypi| |badge_build| |badge_docs| |badge_downloads| **AlphaPy** is a machine learning framework for both speculators and data scientists. It is written in Python with the ``scikit-learn``, @@ -84,3 +84,4 @@ http://alphapy.readthedocs.io/en/latest/introduction/support.html#donations .. |badge_pypi| image:: https://badge.fury.io/py/alphapy.svg .. |badge_build| image:: https://travis-ci.org/ScottfreeLLC/AlphaPy.svg?branch=master .. |badge_docs| image:: https://readthedocs.org/projects/alphapy/badge/?version=latest +.. |badge_downloads| image:: https://pepy.tech/badge/alphapy From 918f84965a532b6869c9e1f7566fb4425e8e5abe Mon Sep 17 00:00:00 2001 From: Mark Conway Date: Sun, 28 Jun 2020 08:01:26 -0400 Subject: [PATCH 082/129] Templates for Bug Reports and Feature Requests Bug Report, Feature Request, and Custom Issue Template --- .github/ISSUE_TEMPLATE/bug_report.md | 38 +++++++++++++++++++++++ .github/ISSUE_TEMPLATE/custom.md | 10 ++++++ .github/ISSUE_TEMPLATE/feature_request.md | 20 ++++++++++++ 3 files changed, 68 insertions(+) create mode 100644 .github/ISSUE_TEMPLATE/bug_report.md create mode 100644 .github/ISSUE_TEMPLATE/custom.md create mode 100644 .github/ISSUE_TEMPLATE/feature_request.md diff --git a/.github/ISSUE_TEMPLATE/bug_report.md b/.github/ISSUE_TEMPLATE/bug_report.md new file mode 100644 index 0000000..dd84ea7 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/bug_report.md @@ -0,0 +1,38 @@ +--- +name: Bug report +about: Create a report to help us improve +title: '' +labels: '' +assignees: '' + +--- + +**Describe the bug** +A clear and concise description of what the bug is. + +**To Reproduce** +Steps to reproduce the behavior: +1. Go to '...' +2. Click on '....' +3. Scroll down to '....' +4. See error + +**Expected behavior** +A clear and concise description of what you expected to happen. + +**Screenshots** +If applicable, add screenshots to help explain your problem. + +**Desktop (please complete the following information):** + - OS: [e.g. iOS] + - Browser [e.g. chrome, safari] + - Version [e.g. 22] + +**Smartphone (please complete the following information):** + - Device: [e.g. iPhone6] + - OS: [e.g. iOS8.1] + - Browser [e.g. stock browser, safari] + - Version [e.g. 22] + +**Additional context** +Add any other context about the problem here. diff --git a/.github/ISSUE_TEMPLATE/custom.md b/.github/ISSUE_TEMPLATE/custom.md new file mode 100644 index 0000000..48d5f81 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/custom.md @@ -0,0 +1,10 @@ +--- +name: Custom issue template +about: Describe this issue template's purpose here. +title: '' +labels: '' +assignees: '' + +--- + + diff --git a/.github/ISSUE_TEMPLATE/feature_request.md b/.github/ISSUE_TEMPLATE/feature_request.md new file mode 100644 index 0000000..bbcbbe7 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/feature_request.md @@ -0,0 +1,20 @@ +--- +name: Feature request +about: Suggest an idea for this project +title: '' +labels: '' +assignees: '' + +--- + +**Is your feature request related to a problem? Please describe.** +A clear and concise description of what the problem is. Ex. I'm always frustrated when [...] + +**Describe the solution you'd like** +A clear and concise description of what you want to happen. + +**Describe alternatives you've considered** +A clear and concise description of any alternative solutions or features you've considered. + +**Additional context** +Add any other context or screenshots about the feature request here. From f680a59df5d624bcc413fa6e55708b2a4eb6cf34 Mon Sep 17 00:00:00 2001 From: Robert Scott Date: Tue, 30 Jun 2020 20:46:45 -0400 Subject: [PATCH 083/129] Update README.rst --- README.rst | 1 + 1 file changed, 1 insertion(+) diff --git a/README.rst b/README.rst index 0434302..9b30bb6 100644 --- a/README.rst +++ b/README.rst @@ -10,6 +10,7 @@ libraries for feature engineering and visualization. Here are just some of the things you can do with AlphaPy: * Run machine learning models using ``scikit-learn``, ``xgboost``, and ``Keras``. +* Generate blended or stacked ensembles. * Create models for analyzing the markets with *MarketFlow*. * Predict sporting events with *SportFlow*. * Develop trading systems and analyze portfolios using *MarketFlow* From 00b04a2a18f9d43a4f369847bf90f77fd6c40c55 Mon Sep 17 00:00:00 2001 From: Mark Conway Date: Mon, 13 Jul 2020 19:36:00 -0400 Subject: [PATCH 084/129] Create FUNDING.yml --- .github/FUNDING.yml | 4 ++++ 1 file changed, 4 insertions(+) create mode 100644 .github/FUNDING.yml diff --git a/.github/FUNDING.yml b/.github/FUNDING.yml new file mode 100644 index 0000000..a7fed05 --- /dev/null +++ b/.github/FUNDING.yml @@ -0,0 +1,4 @@ +# These are supported funding model platforms + +github: [ScottfreeLLC] +custom: ['https://www.paypal.com/donate/?token=nbpC0QxltHUEObSgKi2ckhfyAz1BOa5jcYaaaSa0NwCwBQjl8BJzzxOlAVzx5MRYtA7PS0&country.x=US&locale.x=US'] From f2c91094830d62216e8dfbcf8c12edd2dd78a7e6 Mon Sep 17 00:00:00 2001 From: Mark Conway Date: Mon, 13 Jul 2020 19:40:54 -0400 Subject: [PATCH 085/129] Update FUNDING.yml --- .github/FUNDING.yml | 1 - 1 file changed, 1 deletion(-) diff --git a/.github/FUNDING.yml b/.github/FUNDING.yml index a7fed05..037a123 100644 --- a/.github/FUNDING.yml +++ b/.github/FUNDING.yml @@ -1,4 +1,3 @@ # These are supported funding model platforms github: [ScottfreeLLC] -custom: ['https://www.paypal.com/donate/?token=nbpC0QxltHUEObSgKi2ckhfyAz1BOa5jcYaaaSa0NwCwBQjl8BJzzxOlAVzx5MRYtA7PS0&country.x=US&locale.x=US'] From 718e24601478c5665398cd8841b1c21d10ecdd22 Mon Sep 17 00:00:00 2001 From: Mark Conway Date: Sat, 1 Aug 2020 10:15:55 -0400 Subject: [PATCH 086/129] Create CONTRIBUTING.md --- .github/CONTRIBUTING.md | 37 +++++++++++++++++++++++++++++++++++++ 1 file changed, 37 insertions(+) create mode 100644 .github/CONTRIBUTING.md diff --git a/.github/CONTRIBUTING.md b/.github/CONTRIBUTING.md new file mode 100644 index 0000000..14b82b0 --- /dev/null +++ b/.github/CONTRIBUTING.md @@ -0,0 +1,37 @@ +# Contributing to AlphaPy +Thank you for your contributions. This document discusses: + +- Reporting an issue +- Submitting a fix +- Proposing new features +- Becoming a maintainer + +## Github +We use Github to host our code, to track both issues and feature requests, and also accept pull requests. Pull requests are the best way to propose changes to the codebase ([Github Flow](https://guides.github.com/introduction/flow/index.html)). + +1. Fork the repo and create your branch from `master`. +2. Add tests if possible. +3. If you've changed an API, update the RST documentation. +4. Run your code through lint. +5. Issue the pull request. + +## Contribute under the Apache 2.0 Software License +When you submit code changes, your submissions fall under the [Apache 2.0 License](https://github.com/ScottfreeLLC/AlphaPy/blob/master/LICENSE) that covers the project. + +## Report bugs using Github's [Issues](https://github.com/ScottfreeLLC/AlphaPy/issues) +We use GitHub Issues to track public bugs. Report a bug by [opening a new issue](https://github.com/ScottfreeLLC/AlphaPy/issues). + +## Bug Reports and Feature Requests + +[Bug Report Template](https://github.com/ScottfreeLLC/AlphaPy/blob/master/.github/ISSUE_TEMPLATE/bug_report.md) + +[Feature Request Template](https://github.com/ScottfreeLLC/AlphaPy/blob/master/.github/ISSUE_TEMPLATE/feature_request.md) + +## Coding Style +Please browse the repository to get a sense of how we structure our code and document our functions. + +## License +By contributing, you agree that your contributions will be licensed under its Apache 2.0 License. + +## References +This document was adapted from the open-source contribution guidelines for [Facebook's Draft](https://github.com/facebook/draft-js/blob/a9316a723f9e918afde44dea68b5f9f39b7d9b00/CONTRIBUTING.md). From 5584f92a146fa8b39215a68261374ffe9343e778 Mon Sep 17 00:00:00 2001 From: Mark Conway Date: Sun, 23 Aug 2020 16:18:22 -0400 Subject: [PATCH 087/129] suppress future and deprecation warnings suppress DeprecationWarning and FutureWarning for alphapy, mflow, and sflow --- alphapy/__main__.py | 17 +++++++++++------ alphapy/market_flow.py | 17 +++++++++++------ alphapy/sport_flow.py | 15 +++++++++------ 3 files changed, 31 insertions(+), 18 deletions(-) diff --git a/alphapy/__main__.py b/alphapy/__main__.py index 50230f6..a92ff6c 100644 --- a/alphapy/__main__.py +++ b/alphapy/__main__.py @@ -22,10 +22,21 @@ ################################################################################ +# +# Suppress Warnings +# + +import warnings +warnings.simplefilter(action='ignore', category=DeprecationWarning) +warnings.simplefilter(action='ignore', category=FutureWarning) + + # # Imports # +print(__doc__) + from alphapy.data import get_data from alphapy.data import sample_data from alphapy.data import shuffle_data @@ -68,7 +79,6 @@ import pandas as pd from sklearn.model_selection import train_test_split import sys -import warnings # @@ -446,11 +456,6 @@ def main(args=None): """ - # Suppress Warnings - - warnings.simplefilter(action='ignore', category=DeprecationWarning) - warnings.simplefilter(action='ignore', category=FutureWarning) - # Logging logging.basicConfig(format="[%(asctime)s] %(levelname)s\t%(message)s", diff --git a/alphapy/market_flow.py b/alphapy/market_flow.py index 2c7fcf6..f535d43 100644 --- a/alphapy/market_flow.py +++ b/alphapy/market_flow.py @@ -22,10 +22,21 @@ ################################################################################ +# +# Suppress Warnings +# + +import warnings +warnings.simplefilter(action='ignore', category=DeprecationWarning) +warnings.simplefilter(action='ignore', category=FutureWarning) + + # # Imports # +print(__doc__) + from alphapy.alias import Alias from alphapy.analysis import Analysis from alphapy.analysis import run_analysis @@ -49,7 +60,6 @@ import os import pandas as pd import sys -import warnings import yaml @@ -346,11 +356,6 @@ def main(args=None): """ - # Suppress Warnings - - warnings.simplefilter(action='ignore', category=DeprecationWarning) - warnings.simplefilter(action='ignore', category=FutureWarning) - # Logging logging.basicConfig(format="[%(asctime)s] %(levelname)s\t%(message)s", diff --git a/alphapy/sport_flow.py b/alphapy/sport_flow.py index 1664b40..3d693d7 100644 --- a/alphapy/sport_flow.py +++ b/alphapy/sport_flow.py @@ -22,6 +22,15 @@ ################################################################################ +# +# Suppress Warnings +# + +import warnings +warnings.simplefilter(action='ignore', category=DeprecationWarning) +warnings.simplefilter(action='ignore', category=FutureWarning) + + # # Imports # @@ -49,7 +58,6 @@ import os import pandas as pd import sys -import warnings import yaml @@ -634,11 +642,6 @@ def main(args=None): """ - # Suppress Warnings - - warnings.simplefilter(action='ignore', category=DeprecationWarning) - warnings.simplefilter(action='ignore', category=FutureWarning) - # Logging logging.basicConfig(format="[%(asctime)s] %(levelname)s\t%(message)s", From f24236c7e4dcf1575b8f6bd67c8c35b1a8aa4984 Mon Sep 17 00:00:00 2001 From: Mark Conway Date: Mon, 24 Aug 2020 16:53:06 -0400 Subject: [PATCH 088/129] update dependencies The latest version of TensorFlow 2.3 depends on scipy version 1.4.1. Migrate from Python versions [3.6, 3.7] to [3.7, 3.8]. --- environment.yml | 8 ++++---- setup.py | 10 +++++----- 2 files changed, 9 insertions(+), 9 deletions(-) diff --git a/environment.yml b/environment.yml index ef62905..226952b 100644 --- a/environment.yml +++ b/environment.yml @@ -6,15 +6,15 @@ channels: dependencies: - bokeh>=1.3 - ipython>=7.2 -- keras>=2.2 +- keras>=2.3.1 - matplotlib>=3.0 - numpy>=1.17 - pandas>=1.0 - pyyaml>=5.0 -- scikit-learn>=0.22 -- scipy>=1.1 +- scikit-learn>=0.23.1 +- scipy==1.4.1 - seaborn>=0.9 -- tensorflow>=1.15 +- tensorflow>=2.0 - pip: - arrow>=0.13 - category_encoders>=2.1 diff --git a/setup.py b/setup.py index 568eff7..12225d7 100644 --- a/setup.py +++ b/setup.py @@ -15,8 +15,8 @@ classifiers = ['Development Status :: 4 - Beta', 'Programming Language :: Python', 'Programming Language :: Python :: 3', - 'Programming Language :: Python :: 3.6', 'Programming Language :: Python :: 3.7', + 'Programming Language :: Python :: 3.8', 'License :: OSI Approved :: Apache Software License', 'Intended Audience :: Science/Research', 'Topic :: Scientific/Engineering', @@ -30,17 +30,17 @@ 'iexfinance>=0.4.3', 'imbalanced-learn>=0.5', 'ipython>=7.2', - 'keras>=2.2', + 'keras>=2.3', 'matplotlib>=3.0', 'numpy>=1.17', 'pandas>=1.0', 'pandas-datareader>=0.8', 'pyfolio>=0.9', 'pyyaml>=5.0', - 'scikit-learn>=0.22', - 'scipy>=1.1', + 'scikit-learn>=0.23.1', + 'scipy==1.4.1', 'seaborn>=0.9', - 'tensorflow>=1.15', + 'tensorflow>=2.0', ] if __name__ == "__main__": From 9ecfb20b2e3f24a299e20e04623ed11298d5a97d Mon Sep 17 00:00:00 2001 From: Mark Conway Date: Tue, 25 Aug 2020 19:00:20 -0400 Subject: [PATCH 089/129] sklearn partial dependence moved to inspection module sklearn partial dependence moved to inspection module --- alphapy/plots.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/alphapy/plots.py b/alphapy/plots.py index 8c6bb99..dc91f56 100644 --- a/alphapy/plots.py +++ b/alphapy/plots.py @@ -75,8 +75,8 @@ from scipy import interp import seaborn as sns from sklearn.calibration import calibration_curve -from sklearn.ensemble.partial_dependence import partial_dependence -from sklearn.ensemble.partial_dependence import plot_partial_dependence +from sklearn.inspection import partial_dependence +from sklearn.inspection import plot_partial_dependence from sklearn.metrics import auc from sklearn.metrics import confusion_matrix from sklearn.metrics import roc_curve From ed505aaf5c3ee2bc68319ff995b1b51ceba253cd Mon Sep 17 00:00:00 2001 From: Mark Conway Date: Tue, 25 Aug 2020 19:01:57 -0400 Subject: [PATCH 090/129] fix imblearn.ensemble import fix imblearn.ensemble import --- alphapy/data.py | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/alphapy/data.py b/alphapy/data.py index 2421d2a..b693710 100644 --- a/alphapy/data.py +++ b/alphapy/data.py @@ -43,8 +43,7 @@ from iexfinance.stocks import get_historical_intraday from imblearn.combine import SMOTEENN from imblearn.combine import SMOTETomek -from imblearn.ensemble import BalanceCascade -from imblearn.ensemble import EasyEnsemble +import imblearn.ensemble from imblearn.over_sampling import RandomOverSampler from imblearn.over_sampling import SMOTE from imblearn.under_sampling import ClusterCentroids @@ -269,10 +268,10 @@ def sample_data(model): sampler = SMOTETomek(ratio=ratio) elif sampling_method == SamplingMethod.overunder_smote_enn: sampler = SMOTEENN(ratio=ratio) - elif sampling_method == SamplingMethod.ensemble_easy: - sampler = EasyEnsemble() elif sampling_method == SamplingMethod.ensemble_bc: sampler = BalanceCascade() + elif sampling_method == SamplingMethod.ensemble_easy: + sampler = EasyEnsemble() else: raise ValueError("Unknown Sampling Method %s" % sampling_method) From 50178eb593a821ec072e19ee2e1de270ad093113 Mon Sep 17 00:00:00 2001 From: Mark Conway Date: Tue, 25 Aug 2020 19:16:00 -0400 Subject: [PATCH 091/129] refactor impute_values refactor impute_values and astype(str) fix --- alphapy/features.py | 19 ++++++++++--------- 1 file changed, 10 insertions(+), 9 deletions(-) diff --git a/alphapy/features.py b/alphapy/features.py index f004482..12bfe57 100644 --- a/alphapy/features.py +++ b/alphapy/features.py @@ -266,21 +266,22 @@ def impute_values(feature, dt, sentinel): # for numpy array feature = feature.reshape(-1, 1) - imp = None if dt == 'float64': logger.info(" Imputation for Data Type %s: Median Strategy" % dt) + # replace infinity with imputed value + feature[np.isinf(feature)] = np.nan imp = SimpleImputer(missing_values=np.nan, strategy='median') elif dt == 'int64': logger.info(" Imputation for Data Type %s: Most Frequent Strategy" % dt) imp = SimpleImputer(missing_values=np.nan, strategy='most_frequent') - else: + elif dt != 'bool': logger.info(" Imputation for Data Type %s: Fill Strategy with %d" % (dt, sentinel)) - - if imp: - imputed = imp.fit_transform(feature) + imp = SimpleImputer(missing_values=np.nan, strategy='constant', fill_value=sentinel) else: - feature[np.isnan(feature)] = sentinel - imputed = feature + logger.info(" No Imputation for Data Type %s" % dt) + imp = None + + imputed = imp.fit_transform(feature) if imp else feature return imputed @@ -415,8 +416,8 @@ def get_text_features(fnum, fname, df, nvalues, vectorize, ngrams_max): """ feature = df[fname] - min_length = int(feature.str.len().min()) - max_length = int(feature.str.len().max()) + min_length = int(feature.astype(str).str.len().min()) + max_length = int(feature.astype(str).str.len().max()) if len(feature) == nvalues: logger.info("Feature %d: %s is a text feature [%d:%d] with maximum number of values %d", fnum, fname, min_length, max_length, nvalues) From 6e2e196c1da5c5ecb7bce3c82a10d3721b35b1ad Mon Sep 17 00:00:00 2001 From: Mark Conway Date: Tue, 25 Aug 2020 19:17:01 -0400 Subject: [PATCH 092/129] add day_of_week to extract_date add day_of_week to extract_date and astype(str) fix --- alphapy/transforms.py | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/alphapy/transforms.py b/alphapy/transforms.py index fd3cf76..08e7889 100644 --- a/alphapy/transforms.py +++ b/alphapy/transforms.py @@ -487,7 +487,7 @@ def extract_bizday(f, c): # def extract_date(f, c): - r"""Extract date into its components: year, month, day. + r"""Extract date into its components: year, month, day, dayofweek. Parameters ---------- @@ -508,7 +508,8 @@ def extract_date(f, c): fyear = pd.Series(fc.dt.year, name='year') fmonth = pd.Series(fc.dt.month, name='month') fday = pd.Series(fc.dt.day, name='day') - frames = [fyear, fmonth, fday] + fdow = pd.Series(fc.dt.dayofweek, name='day_of_week') + frames = [fyear, fmonth, fday, fdow] date_features = pd.concat(frames, axis=1) except: logger.info("Could not extract date information from %s column", c) @@ -1345,7 +1346,7 @@ def split_to_letters(f, c): dtype = fc.dtypes if dtype == 'object': fc.fillna(NULLTEXT, inplace=True) - maxlen = fc.str.len().max() + maxlen = fc.astype(str).str.len().max() if maxlen > 1: new_feature = fc.apply(lambda x: BSEP.join(list(x))) return new_feature @@ -1417,7 +1418,7 @@ def texplode(f, c): """ fc = f[c] - maxlen = fc.str.len().max() + maxlen = fc.astype(str).str.len().max() fc.fillna(maxlen * BSEP, inplace=True) fpad = str().join(['{:', BSEP, '>', str(maxlen), '}']) fcpad = fc.apply(fpad.format) From b0c36581cf891c3b4253d9ea6229e35b887b95ff Mon Sep 17 00:00:00 2001 From: Mark Conway Date: Tue, 25 Aug 2020 19:28:04 -0400 Subject: [PATCH 093/129] replace xgboost silent option with verbosity replace xgboost silent option with verbosity --- alphapy/examples/Kaggle/config/algos.yml | 6 +++--- alphapy/examples/NCAAB/config/algos.yml | 6 +++--- alphapy/examples/Trading Model/config/algos.yml | 6 +++--- alphapy/examples/Trading System/config/algos.yml | 6 +++--- 4 files changed, 12 insertions(+), 12 deletions(-) diff --git a/alphapy/examples/Kaggle/config/algos.yml b/alphapy/examples/Kaggle/config/algos.yml index 17ebce6..33c287f 100644 --- a/alphapy/examples/Kaggle/config/algos.yml +++ b/alphapy/examples/Kaggle/config/algos.yml @@ -192,7 +192,7 @@ XGB: "subsample" : 1.0, "colsample_bytree" : 1.0, "nthread" : n_jobs, - "silent" : True} + "verbosity" : 0} grid : {"n_estimators" : [21, 51, 101, 201, 501], "max_depth" : [5, 6, 7, 8, 9, 10, 12, 15, 20], "learning_rate" : [0.01, 0.02, 0.05, 0.1, 0.2], @@ -212,7 +212,7 @@ XGBM: "subsample" : 0.9, "colsample_bytree" : 0.9, "nthread" : n_jobs, - "silent" : True} + "verbosity" : 0} grid : {} XGBR: @@ -228,7 +228,7 @@ XGBR: "colsample_bytree" : 0.9, "seed" : seed, "nthread" : n_jobs, - "silent" : True} + "verbosity" : 0} grid : {} XT: diff --git a/alphapy/examples/NCAAB/config/algos.yml b/alphapy/examples/NCAAB/config/algos.yml index 17ebce6..33c287f 100644 --- a/alphapy/examples/NCAAB/config/algos.yml +++ b/alphapy/examples/NCAAB/config/algos.yml @@ -192,7 +192,7 @@ XGB: "subsample" : 1.0, "colsample_bytree" : 1.0, "nthread" : n_jobs, - "silent" : True} + "verbosity" : 0} grid : {"n_estimators" : [21, 51, 101, 201, 501], "max_depth" : [5, 6, 7, 8, 9, 10, 12, 15, 20], "learning_rate" : [0.01, 0.02, 0.05, 0.1, 0.2], @@ -212,7 +212,7 @@ XGBM: "subsample" : 0.9, "colsample_bytree" : 0.9, "nthread" : n_jobs, - "silent" : True} + "verbosity" : 0} grid : {} XGBR: @@ -228,7 +228,7 @@ XGBR: "colsample_bytree" : 0.9, "seed" : seed, "nthread" : n_jobs, - "silent" : True} + "verbosity" : 0} grid : {} XT: diff --git a/alphapy/examples/Trading Model/config/algos.yml b/alphapy/examples/Trading Model/config/algos.yml index 17ebce6..33c287f 100644 --- a/alphapy/examples/Trading Model/config/algos.yml +++ b/alphapy/examples/Trading Model/config/algos.yml @@ -192,7 +192,7 @@ XGB: "subsample" : 1.0, "colsample_bytree" : 1.0, "nthread" : n_jobs, - "silent" : True} + "verbosity" : 0} grid : {"n_estimators" : [21, 51, 101, 201, 501], "max_depth" : [5, 6, 7, 8, 9, 10, 12, 15, 20], "learning_rate" : [0.01, 0.02, 0.05, 0.1, 0.2], @@ -212,7 +212,7 @@ XGBM: "subsample" : 0.9, "colsample_bytree" : 0.9, "nthread" : n_jobs, - "silent" : True} + "verbosity" : 0} grid : {} XGBR: @@ -228,7 +228,7 @@ XGBR: "colsample_bytree" : 0.9, "seed" : seed, "nthread" : n_jobs, - "silent" : True} + "verbosity" : 0} grid : {} XT: diff --git a/alphapy/examples/Trading System/config/algos.yml b/alphapy/examples/Trading System/config/algos.yml index 17ebce6..33c287f 100644 --- a/alphapy/examples/Trading System/config/algos.yml +++ b/alphapy/examples/Trading System/config/algos.yml @@ -192,7 +192,7 @@ XGB: "subsample" : 1.0, "colsample_bytree" : 1.0, "nthread" : n_jobs, - "silent" : True} + "verbosity" : 0} grid : {"n_estimators" : [21, 51, 101, 201, 501], "max_depth" : [5, 6, 7, 8, 9, 10, 12, 15, 20], "learning_rate" : [0.01, 0.02, 0.05, 0.1, 0.2], @@ -212,7 +212,7 @@ XGBM: "subsample" : 0.9, "colsample_bytree" : 0.9, "nthread" : n_jobs, - "silent" : True} + "verbosity" : 0} grid : {} XGBR: @@ -228,7 +228,7 @@ XGBR: "colsample_bytree" : 0.9, "seed" : seed, "nthread" : n_jobs, - "silent" : True} + "verbosity" : 0} grid : {} XT: From d1faa8bb6f53a3bd46575ce4a353bf2b62a3c469 Mon Sep 17 00:00:00 2001 From: Mark Conway Date: Tue, 25 Aug 2020 19:31:10 -0400 Subject: [PATCH 094/129] update to version 2.4.3 update to version 2.4.3 --- docs/conf.py | 4 ++-- setup.py | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/docs/conf.py b/docs/conf.py index 719aa58..2fa5fce 100644 --- a/docs/conf.py +++ b/docs/conf.py @@ -62,9 +62,9 @@ # built documents. # # The short X.Y version. -version = '2.4.2' +version = '2.4.3' # The full version, including alpha/beta/rc tags. -release = '2.4.2' +release = '2.4.3' # The language for content autogenerated by Sphinx. Refer to documentation # for a list of supported languages. diff --git a/setup.py b/setup.py index 12225d7..2a2e6eb 100644 --- a/setup.py +++ b/setup.py @@ -10,7 +10,7 @@ MAINTAINER_EMAIL = 'scottfree.analytics@scottfreellc.com' URL = "https://github.com/ScottFreeLLC/AlphaPy" LICENSE = "Apache License, Version 2" -VERSION = "2.4.2" +VERSION = "2.4.3" classifiers = ['Development Status :: 4 - Beta', 'Programming Language :: Python', From 05177a3843178e1ef817116ea511f7e4a50e2a2d Mon Sep 17 00:00:00 2001 From: Mark Conway Date: Thu, 27 Aug 2020 01:33:13 -0400 Subject: [PATCH 095/129] Python 3.8 Python 3.8 --- .travis.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.travis.yml b/.travis.yml index e9e8231..be55dc5 100644 --- a/.travis.yml +++ b/.travis.yml @@ -2,8 +2,8 @@ language: python sudo: false python: - - "3.6" - "3.7" + - "3.8" before_install: # We do this conditionally because it saves us some downloading if the From 21a40fb8a47761900d3f6eb2d37db260f292a482 Mon Sep 17 00:00:00 2001 From: Mark Conway Date: Sat, 29 Aug 2020 08:50:15 -0400 Subject: [PATCH 096/129] update algorithm files for LightGBM and CatBoost add updated algorithm files (algos.yml) in preparation for LightGBM and CatBoost --- alphapy/examples/Kaggle/config/algos.yml | 47 ++++++++++++++++--- alphapy/examples/NCAAB/config/algos.yml | 47 ++++++++++++++++--- .../examples/Trading Model/config/algos.yml | 47 ++++++++++++++++--- .../examples/Trading System/config/algos.yml | 47 ++++++++++++++++--- 4 files changed, 164 insertions(+), 24 deletions(-) diff --git a/alphapy/examples/Kaggle/config/algos.yml b/alphapy/examples/Kaggle/config/algos.yml index 33c287f..3850f65 100644 --- a/alphapy/examples/Kaggle/config/algos.yml +++ b/alphapy/examples/Kaggle/config/algos.yml @@ -11,6 +11,24 @@ AB: "learning_rate" : [0.2, 0.5, 0.7, 1.0, 1.5, 2.0], "algorithm" : ['SAMME', 'SAMME.R']} +CATB: + # CatBoost Binary + model_type : classification + params : {"iterations" : n_estimators, + "random_seed" : seed, + "thread_count" : n_jobs, + "verbose" : verbosity} + grid : {} + +CATBR: + # CatBoost Regression + model_type : regression + params : {"iterations" : n_estimators, + "random_seed" : seed, + "thread_count" : n_jobs, + "verbose" : verbosity} + grid : {} + GB: # Gradient Boosting model_type : classification @@ -43,7 +61,7 @@ KERASC: "metrics" : 'accuracy'} params : {"epochs" : 50, "batch_size" : 10, - "verbose" : 1} + "verbose" : verbosity} grid : {} KERASR: @@ -55,7 +73,7 @@ KERASR: "loss" : 'mse'} params : {"epochs" : 50, "batch_size" : 10, - "verbose" : 1} + "verbose" : verbosity} grid : {} KNN: @@ -73,6 +91,24 @@ KNR: params : {"n_jobs" : n_jobs} grid : {} +LGB: + # LightGBM Binary + model_type : classification + params : {"n_estimators" : n_estimators, + "random_state" : seed, + "n_jobs" : n_jobs, + "silent" : verbosity} + grid : {} + +LGBR: + # LightGBM Regression + model_type : regression + params : {"n_estimators" : n_estimators, + "random_state" : seed, + "n_jobs" : n_jobs, + "silent" : verbosity} + grid : {} + LOGR: # Logistic Regression model_type : classification @@ -192,7 +228,7 @@ XGB: "subsample" : 1.0, "colsample_bytree" : 1.0, "nthread" : n_jobs, - "verbosity" : 0} + "verbosity" : verbosity} grid : {"n_estimators" : [21, 51, 101, 201, 501], "max_depth" : [5, 6, 7, 8, 9, 10, 12, 15, 20], "learning_rate" : [0.01, 0.02, 0.05, 0.1, 0.2], @@ -212,7 +248,7 @@ XGBM: "subsample" : 0.9, "colsample_bytree" : 0.9, "nthread" : n_jobs, - "verbosity" : 0} + "verbosity" : verbosity} grid : {} XGBR: @@ -220,7 +256,6 @@ XGBR: model_type : regression params : {"objective" : 'reg:linear', "n_estimators" : n_estimators, - "seed" : seed, "max_depth" : 10, "learning_rate" : 0.1, "min_child_weight" : 1.1, @@ -228,7 +263,7 @@ XGBR: "colsample_bytree" : 0.9, "seed" : seed, "nthread" : n_jobs, - "verbosity" : 0} + "verbosity" : verbosity} grid : {} XT: diff --git a/alphapy/examples/NCAAB/config/algos.yml b/alphapy/examples/NCAAB/config/algos.yml index 33c287f..3850f65 100644 --- a/alphapy/examples/NCAAB/config/algos.yml +++ b/alphapy/examples/NCAAB/config/algos.yml @@ -11,6 +11,24 @@ AB: "learning_rate" : [0.2, 0.5, 0.7, 1.0, 1.5, 2.0], "algorithm" : ['SAMME', 'SAMME.R']} +CATB: + # CatBoost Binary + model_type : classification + params : {"iterations" : n_estimators, + "random_seed" : seed, + "thread_count" : n_jobs, + "verbose" : verbosity} + grid : {} + +CATBR: + # CatBoost Regression + model_type : regression + params : {"iterations" : n_estimators, + "random_seed" : seed, + "thread_count" : n_jobs, + "verbose" : verbosity} + grid : {} + GB: # Gradient Boosting model_type : classification @@ -43,7 +61,7 @@ KERASC: "metrics" : 'accuracy'} params : {"epochs" : 50, "batch_size" : 10, - "verbose" : 1} + "verbose" : verbosity} grid : {} KERASR: @@ -55,7 +73,7 @@ KERASR: "loss" : 'mse'} params : {"epochs" : 50, "batch_size" : 10, - "verbose" : 1} + "verbose" : verbosity} grid : {} KNN: @@ -73,6 +91,24 @@ KNR: params : {"n_jobs" : n_jobs} grid : {} +LGB: + # LightGBM Binary + model_type : classification + params : {"n_estimators" : n_estimators, + "random_state" : seed, + "n_jobs" : n_jobs, + "silent" : verbosity} + grid : {} + +LGBR: + # LightGBM Regression + model_type : regression + params : {"n_estimators" : n_estimators, + "random_state" : seed, + "n_jobs" : n_jobs, + "silent" : verbosity} + grid : {} + LOGR: # Logistic Regression model_type : classification @@ -192,7 +228,7 @@ XGB: "subsample" : 1.0, "colsample_bytree" : 1.0, "nthread" : n_jobs, - "verbosity" : 0} + "verbosity" : verbosity} grid : {"n_estimators" : [21, 51, 101, 201, 501], "max_depth" : [5, 6, 7, 8, 9, 10, 12, 15, 20], "learning_rate" : [0.01, 0.02, 0.05, 0.1, 0.2], @@ -212,7 +248,7 @@ XGBM: "subsample" : 0.9, "colsample_bytree" : 0.9, "nthread" : n_jobs, - "verbosity" : 0} + "verbosity" : verbosity} grid : {} XGBR: @@ -220,7 +256,6 @@ XGBR: model_type : regression params : {"objective" : 'reg:linear', "n_estimators" : n_estimators, - "seed" : seed, "max_depth" : 10, "learning_rate" : 0.1, "min_child_weight" : 1.1, @@ -228,7 +263,7 @@ XGBR: "colsample_bytree" : 0.9, "seed" : seed, "nthread" : n_jobs, - "verbosity" : 0} + "verbosity" : verbosity} grid : {} XT: diff --git a/alphapy/examples/Trading Model/config/algos.yml b/alphapy/examples/Trading Model/config/algos.yml index 33c287f..3850f65 100644 --- a/alphapy/examples/Trading Model/config/algos.yml +++ b/alphapy/examples/Trading Model/config/algos.yml @@ -11,6 +11,24 @@ AB: "learning_rate" : [0.2, 0.5, 0.7, 1.0, 1.5, 2.0], "algorithm" : ['SAMME', 'SAMME.R']} +CATB: + # CatBoost Binary + model_type : classification + params : {"iterations" : n_estimators, + "random_seed" : seed, + "thread_count" : n_jobs, + "verbose" : verbosity} + grid : {} + +CATBR: + # CatBoost Regression + model_type : regression + params : {"iterations" : n_estimators, + "random_seed" : seed, + "thread_count" : n_jobs, + "verbose" : verbosity} + grid : {} + GB: # Gradient Boosting model_type : classification @@ -43,7 +61,7 @@ KERASC: "metrics" : 'accuracy'} params : {"epochs" : 50, "batch_size" : 10, - "verbose" : 1} + "verbose" : verbosity} grid : {} KERASR: @@ -55,7 +73,7 @@ KERASR: "loss" : 'mse'} params : {"epochs" : 50, "batch_size" : 10, - "verbose" : 1} + "verbose" : verbosity} grid : {} KNN: @@ -73,6 +91,24 @@ KNR: params : {"n_jobs" : n_jobs} grid : {} +LGB: + # LightGBM Binary + model_type : classification + params : {"n_estimators" : n_estimators, + "random_state" : seed, + "n_jobs" : n_jobs, + "silent" : verbosity} + grid : {} + +LGBR: + # LightGBM Regression + model_type : regression + params : {"n_estimators" : n_estimators, + "random_state" : seed, + "n_jobs" : n_jobs, + "silent" : verbosity} + grid : {} + LOGR: # Logistic Regression model_type : classification @@ -192,7 +228,7 @@ XGB: "subsample" : 1.0, "colsample_bytree" : 1.0, "nthread" : n_jobs, - "verbosity" : 0} + "verbosity" : verbosity} grid : {"n_estimators" : [21, 51, 101, 201, 501], "max_depth" : [5, 6, 7, 8, 9, 10, 12, 15, 20], "learning_rate" : [0.01, 0.02, 0.05, 0.1, 0.2], @@ -212,7 +248,7 @@ XGBM: "subsample" : 0.9, "colsample_bytree" : 0.9, "nthread" : n_jobs, - "verbosity" : 0} + "verbosity" : verbosity} grid : {} XGBR: @@ -220,7 +256,6 @@ XGBR: model_type : regression params : {"objective" : 'reg:linear', "n_estimators" : n_estimators, - "seed" : seed, "max_depth" : 10, "learning_rate" : 0.1, "min_child_weight" : 1.1, @@ -228,7 +263,7 @@ XGBR: "colsample_bytree" : 0.9, "seed" : seed, "nthread" : n_jobs, - "verbosity" : 0} + "verbosity" : verbosity} grid : {} XT: diff --git a/alphapy/examples/Trading System/config/algos.yml b/alphapy/examples/Trading System/config/algos.yml index 33c287f..3850f65 100644 --- a/alphapy/examples/Trading System/config/algos.yml +++ b/alphapy/examples/Trading System/config/algos.yml @@ -11,6 +11,24 @@ AB: "learning_rate" : [0.2, 0.5, 0.7, 1.0, 1.5, 2.0], "algorithm" : ['SAMME', 'SAMME.R']} +CATB: + # CatBoost Binary + model_type : classification + params : {"iterations" : n_estimators, + "random_seed" : seed, + "thread_count" : n_jobs, + "verbose" : verbosity} + grid : {} + +CATBR: + # CatBoost Regression + model_type : regression + params : {"iterations" : n_estimators, + "random_seed" : seed, + "thread_count" : n_jobs, + "verbose" : verbosity} + grid : {} + GB: # Gradient Boosting model_type : classification @@ -43,7 +61,7 @@ KERASC: "metrics" : 'accuracy'} params : {"epochs" : 50, "batch_size" : 10, - "verbose" : 1} + "verbose" : verbosity} grid : {} KERASR: @@ -55,7 +73,7 @@ KERASR: "loss" : 'mse'} params : {"epochs" : 50, "batch_size" : 10, - "verbose" : 1} + "verbose" : verbosity} grid : {} KNN: @@ -73,6 +91,24 @@ KNR: params : {"n_jobs" : n_jobs} grid : {} +LGB: + # LightGBM Binary + model_type : classification + params : {"n_estimators" : n_estimators, + "random_state" : seed, + "n_jobs" : n_jobs, + "silent" : verbosity} + grid : {} + +LGBR: + # LightGBM Regression + model_type : regression + params : {"n_estimators" : n_estimators, + "random_state" : seed, + "n_jobs" : n_jobs, + "silent" : verbosity} + grid : {} + LOGR: # Logistic Regression model_type : classification @@ -192,7 +228,7 @@ XGB: "subsample" : 1.0, "colsample_bytree" : 1.0, "nthread" : n_jobs, - "verbosity" : 0} + "verbosity" : verbosity} grid : {"n_estimators" : [21, 51, 101, 201, 501], "max_depth" : [5, 6, 7, 8, 9, 10, 12, 15, 20], "learning_rate" : [0.01, 0.02, 0.05, 0.1, 0.2], @@ -212,7 +248,7 @@ XGBM: "subsample" : 0.9, "colsample_bytree" : 0.9, "nthread" : n_jobs, - "verbosity" : 0} + "verbosity" : verbosity} grid : {} XGBR: @@ -220,7 +256,6 @@ XGBR: model_type : regression params : {"objective" : 'reg:linear', "n_estimators" : n_estimators, - "seed" : seed, "max_depth" : 10, "learning_rate" : 0.1, "min_child_weight" : 1.1, @@ -228,7 +263,7 @@ XGBR: "colsample_bytree" : 0.9, "seed" : seed, "nthread" : n_jobs, - "verbosity" : 0} + "verbosity" : verbosity} grid : {} XT: From 2a3824b776a6f5d803fff2a33573ac0b7eb9600e Mon Sep 17 00:00:00 2001 From: Mark Conway Date: Sat, 29 Aug 2020 14:37:22 -0400 Subject: [PATCH 097/129] LightGBM and CatBoost LightGBM and CatBoost --- README.rst | 31 +++++--- alphapy/estimators.py | 94 ++++++++++++++++++------ alphapy/examples/Kaggle/config/model.yml | 10 +-- 3 files changed, 97 insertions(+), 38 deletions(-) diff --git a/README.rst b/README.rst index 9b30bb6..d3e1e5d 100644 --- a/README.rst +++ b/README.rst @@ -4,12 +4,13 @@ AlphaPy |badge_pypi| |badge_build| |badge_docs| |badge_downloads| **AlphaPy** is a machine learning framework for both speculators and -data scientists. It is written in Python with the ``scikit-learn``, -``pandas``, and ``Keras`` libraries, as well as many other helpful -libraries for feature engineering and visualization. Here are just +data scientists. It is written in Python mainly with the ``scikit-learn`` +and ``pandas`` libraries, as well as many other helpful +packages for feature engineering and visualization. Here are just some of the things you can do with AlphaPy: -* Run machine learning models using ``scikit-learn``, ``xgboost``, and ``Keras``. +* Run machine learning models using ``scikit-learn``, ``Keras``, ``xgboost``, +``LightGBM``, and ``CatBoost``. * Generate blended or stacked ensembles. * Create models for analyzing the markets with *MarketFlow*. * Predict sporting events with *SportFlow*. @@ -21,11 +22,16 @@ some of the things you can do with AlphaPy: :alt: AlphaPy Model Pipeline :align: center +Documentation +------------- + +http://alphapy.readthedocs.io/en/latest/ + Installation ------------ -You should already have pip, Python, and XGBoost (see below) -installed on your system. Run the following command to install +You should already have pip, Python, and optionally XGBoost, LightGBM, and +CatBoost installed on your system (see below). Run the following command to install AlphaPy:: pip install -U alphapy @@ -37,10 +43,17 @@ For Mac and Windows users, XGBoost will *not* install automatically with ``pip``. For instructions to install XGBoost on your specific platform, go to http://xgboost.readthedocs.io/en/latest/build.html. -Documentation -------------- +LightGBM +~~~~~~~~ -http://alphapy.readthedocs.io/en/latest/ +For instructions to install LightGBM on your specific +platform, go to https://lightgbm.readthedocs.io/en/latest/Installation-Guide.html. + +CatBoost +~~~~~~~~ + +For instructions to install CatBoost on your specific +platform, go to https://catboost.ai/docs/concepts/python-installation.html. MarketFlow ---------- diff --git a/alphapy/estimators.py b/alphapy/estimators.py index 1b10ee2..4394dba 100644 --- a/alphapy/estimators.py +++ b/alphapy/estimators.py @@ -51,7 +51,7 @@ from sklearn.neighbors import KNeighborsRegressor from sklearn.svm import LinearSVC from sklearn.svm import SVC -import xgboost as xgb +import sys import yaml @@ -166,14 +166,45 @@ def __str__(self): 'RF' : RandomForestClassifier, 'RFR' : RandomForestRegressor, 'SVM' : SVC, - 'XGB' : xgb.XGBClassifier, - 'XGBM' : xgb.XGBClassifier, - 'XGBR' : xgb.XGBRegressor, 'XT' : ExtraTreesClassifier, 'XTR' : ExtraTreesRegressor } +# +# Find optional packages +# + +def find_optional_packages(): + + module_name = 'xgboost' + try: + import xgboost as xgb + estimator_map['XGB'] = xgb.XGBClassifier + estimator_map['XGBM'] = xgb.XGBClassifier + estimator_map['XGBR'] = xgb.XGBRegressor + except: + logger.info("Cannot load %s" % module_name) + + module_name = 'lightgbm' + try: + import lightgbm as lgb + estimator_map['LGB'] = lgb.LGBMClassifier + estimator_map['LGBR'] = lgb.LGBMRegressor + except: + logger.info("Cannot load %s" % module_name) + + module_name = 'catboost' + try: + import catboost as catb + estimator_map['CATB'] = catb.CatBoostClassifier + estimator_map['CATBR'] = catb.CatBoostRegressor + except: + logger.info("Cannot load %s" % module_name) + + return + + # # Function get_algos_config # @@ -202,6 +233,10 @@ def get_algos_config(cfg_dir): with open(full_path, 'r') as ymlfile: specs = yaml.load(ymlfile, Loader=yaml.FullLoader) + # Find optional packages + + find_optional_packages() + # Ensure each algorithm has required keys minimum_keys = ['model_type', 'params', 'grid'] @@ -315,10 +350,14 @@ def get_estimators(model): # Global parameter substitution fields ps_fields = {'n_estimators' : 'n_estimators', + 'iterations' : 'n_estimators', 'n_jobs' : 'n_jobs', 'nthread' : 'n_jobs', - 'random_state' : 'seed', + 'thread_count' : 'n_jobs', 'seed' : 'seed', + 'random_state' : 'seed', + 'random_seed' : 'seed', + 'verbosity' : 'verbosity', 'verbose' : 'verbosity'} # Get algorithm specifications @@ -334,25 +373,32 @@ def get_estimators(model): for param in params: if param in ps_fields and isinstance(param, str): algo_specs[algo]['params'][param] = eval(ps_fields[param]) - func = estimator_map[algo] - if 'KERAS' in algo: - params['build_fn'] = create_keras_model - layers = algo_specs[algo]['layers'] - params['nlayers'] = len(layers) - input_dim_string = ', input_dim={})'.format(X_train.shape[1]) - layers[0] = layers[0].replace(')', input_dim_string) - for i, layer in enumerate(layers): - params['layer'+str(i+1)] = layer - compiler = algo_specs[algo]['compiler'] - params['optimizer'] = compiler['optimizer'] - params['loss'] = compiler['loss'] - try: - params['metrics'] = compiler['metrics'] - except: - pass - est = func(**params) - grid = algo_specs[algo]['grid'] - estimators[algo] = Estimator(algo, model_type, est, grid) + try: + algo_found = True + func = estimator_map[algo] + except: + algo_found = False + logger.info("Algorithm %s not found (check package installation)" % algo) + if algo_found: + if 'KERAS' in algo: + params['build_fn'] = create_keras_model + layers = algo_specs[algo]['layers'] + params['nlayers'] = len(layers) + input_dim_string = ', input_dim={})'.format(X_train.shape[1]) + layers[0] = layers[0].replace(')', input_dim_string) + for i, layer in enumerate(layers): + params['layer'+str(i+1)] = layer + compiler = algo_specs[algo]['compiler'] + params['optimizer'] = compiler['optimizer'] + params['loss'] = compiler['loss'] + try: + params['metrics'] = compiler['metrics'] + except: + pass + est = func(**params) + grid = algo_specs[algo]['grid'] + estimators[algo] = Estimator(algo, model_type, est, grid) + # return the entire classifier list return estimators diff --git a/alphapy/examples/Kaggle/config/model.yml b/alphapy/examples/Kaggle/config/model.yml index ac84366..f8ba9d1 100644 --- a/alphapy/examples/Kaggle/config/model.yml +++ b/alphapy/examples/Kaggle/config/model.yml @@ -19,7 +19,7 @@ data: target_value : 1 model: - algorithms : ['RF', 'XGB'] + algorithms : ['CATB', 'KERASC', 'LGB', 'XGB'] balance_classes : True calibration : option : False @@ -55,10 +55,10 @@ features: encoding : rounding : 2 type : target - factors : [] + factors : ['Embarked'] interactions : - option : True - poly_degree : 5 + option : False + poly_degree : 2 sampling_pct : 10 isomap : option : False @@ -80,7 +80,7 @@ features: scipy : option : False text : - ngrams : 3 + ngrams : 2 vectorize : False tsne : option : False From e39d511f6f5ca28e502d112596dc2b53573a414e Mon Sep 17 00:00:00 2001 From: Mark Conway Date: Sat, 29 Aug 2020 14:41:39 -0400 Subject: [PATCH 098/129] update to version 2.5.0 update to version 2.5.0 --- docs/conf.py | 6 +++--- setup.py | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/docs/conf.py b/docs/conf.py index 2fa5fce..5609f5a 100644 --- a/docs/conf.py +++ b/docs/conf.py @@ -62,9 +62,9 @@ # built documents. # # The short X.Y version. -version = '2.4.3' +version = '2.5.0' # The full version, including alpha/beta/rc tags. -release = '2.4.3' +release = '2.5.0' # The language for content autogenerated by Sphinx. Refer to documentation # for a list of supported languages. @@ -156,6 +156,6 @@ # dir menu entry, description, category) texinfo_documents = [ (master_doc, 'AlphaPy', 'AlphaPy Documentation', - author, 'AlphaPy', 'AutoML for Stocks and Sports', + author, 'AlphaPy', 'AutoML for Data Scientists and Speculators', 'Miscellaneous'), ] diff --git a/setup.py b/setup.py index 2a2e6eb..f9e3dbd 100644 --- a/setup.py +++ b/setup.py @@ -10,7 +10,7 @@ MAINTAINER_EMAIL = 'scottfree.analytics@scottfreellc.com' URL = "https://github.com/ScottFreeLLC/AlphaPy" LICENSE = "Apache License, Version 2" -VERSION = "2.4.3" +VERSION = "2.5.0" classifiers = ['Development Status :: 4 - Beta', 'Programming Language :: Python', From a87c188135486764b5759772a9ab7ad5fbd75c5d Mon Sep 17 00:00:00 2001 From: Mark Conway Date: Sat, 29 Aug 2020 14:42:51 -0400 Subject: [PATCH 099/129] Update README.rst --- README.rst | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/README.rst b/README.rst index d3e1e5d..c02556f 100644 --- a/README.rst +++ b/README.rst @@ -9,8 +9,7 @@ and ``pandas`` libraries, as well as many other helpful packages for feature engineering and visualization. Here are just some of the things you can do with AlphaPy: -* Run machine learning models using ``scikit-learn``, ``Keras``, ``xgboost``, -``LightGBM``, and ``CatBoost``. +* Run machine learning models using ``scikit-learn``, ``Keras``, ``xgboost``, ``LightGBM``, and ``CatBoost``. * Generate blended or stacked ensembles. * Create models for analyzing the markets with *MarketFlow*. * Predict sporting events with *SportFlow*. From b4bf4614382cf2a0f42b2fd872a1fbcb5982850b Mon Sep 17 00:00:00 2001 From: Mark Conway Date: Sun, 6 Sep 2020 08:58:25 -0400 Subject: [PATCH 100/129] Pyfolio Tear Sheet Fix Pyfolio Tear Sheet Fix --- README.rst | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/README.rst b/README.rst index c02556f..8cc049a 100644 --- a/README.rst +++ b/README.rst @@ -35,6 +35,18 @@ AlphaPy:: pip install -U alphapy +Pyfolio +~~~~~~~ + +Pyfolio is automatically installed by AlphaPy, but if you encounter +the following error when trying to create a tear sheet: + + *AttributeError: 'numpy.int64' object has no attribute 'to_pydatetime'* + +Install pyfolio with this command: + + pip install git+https://github.com/quantopian/pyfolio + XGBoost ~~~~~~~ From d2d099527ca34d41c87bf9a1a2a0a65a2cd0bf9e Mon Sep 17 00:00:00 2001 From: Marc Conway Date: Mon, 8 Feb 2021 16:32:22 -0500 Subject: [PATCH 101/129] Update README.rst --- README.rst | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/README.rst b/README.rst index 8cc049a..d58c4b3 100644 --- a/README.rst +++ b/README.rst @@ -1,7 +1,7 @@ AlphaPy ======= -|badge_pypi| |badge_build| |badge_docs| |badge_downloads| +|badge_pypi| |badge_build| |badge_docs| |badge_downloads| |badge_hits| **AlphaPy** is a machine learning framework for both speculators and data scientists. It is written in Python mainly with the ``scikit-learn`` @@ -110,3 +110,4 @@ http://alphapy.readthedocs.io/en/latest/introduction/support.html#donations .. |badge_build| image:: https://travis-ci.org/ScottfreeLLC/AlphaPy.svg?branch=master .. |badge_docs| image:: https://readthedocs.org/projects/alphapy/badge/?version=latest .. |badge_downloads| image:: https://pepy.tech/badge/alphapy +.. [badge_hits] [![HitCount](http://hits.dwyl.com/ScottfreeLLC/https://githubcom/ScottfreeLLC/AlphaPy.svg)](http://hits.dwyl.com/ScottfreeLLC/https://githubcom/ScottfreeLLC/AlphaPy) From fb0a52a822f3c5c58a9adf4c038204dea7d78f29 Mon Sep 17 00:00:00 2001 From: Marc Conway Date: Mon, 8 Feb 2021 16:35:40 -0500 Subject: [PATCH 102/129] Update README.rst --- README.rst | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/README.rst b/README.rst index d58c4b3..8cc049a 100644 --- a/README.rst +++ b/README.rst @@ -1,7 +1,7 @@ AlphaPy ======= -|badge_pypi| |badge_build| |badge_docs| |badge_downloads| |badge_hits| +|badge_pypi| |badge_build| |badge_docs| |badge_downloads| **AlphaPy** is a machine learning framework for both speculators and data scientists. It is written in Python mainly with the ``scikit-learn`` @@ -110,4 +110,3 @@ http://alphapy.readthedocs.io/en/latest/introduction/support.html#donations .. |badge_build| image:: https://travis-ci.org/ScottfreeLLC/AlphaPy.svg?branch=master .. |badge_docs| image:: https://readthedocs.org/projects/alphapy/badge/?version=latest .. |badge_downloads| image:: https://pepy.tech/badge/alphapy -.. [badge_hits] [![HitCount](http://hits.dwyl.com/ScottfreeLLC/https://githubcom/ScottfreeLLC/AlphaPy.svg)](http://hits.dwyl.com/ScottfreeLLC/https://githubcom/ScottfreeLLC/AlphaPy) From 89c8204c9ab8f948facf853b38846dcf7cd88da9 Mon Sep 17 00:00:00 2001 From: Mark Conway Date: Sat, 23 Oct 2021 03:15:02 -0400 Subject: [PATCH 103/129] Update .gitignore --- .gitignore | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/.gitignore b/.gitignore index 6453caf..e42261c 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,5 @@ +.DS_Store + *.pyc *.iml @@ -17,8 +19,8 @@ alphapy/examples/Trading System/.ipynb_checkpoints/A Trading System-checkpoint.ipynb *.pkl *.png -*.code-workspace -alphapy/.vscode/launch.json -alphapy/.vscode/settings.json -*.log -docs/.vscode/settings.json +*.code-workspace +alphapy/.vscode/launch.json +alphapy/.vscode/settings.json +*.log +docs/.vscode/settings.json From 83185930a5e4ca7afeb90aab45c499dad164cf84 Mon Sep 17 00:00:00 2001 From: Mark Conway Date: Sat, 23 Oct 2021 03:15:43 -0400 Subject: [PATCH 104/129] Cleanup Cleanup --- .DS_Store | Bin 0 -> 6148 bytes alphapy/.DS_Store | Bin 0 -> 6148 bytes alphapy/examples/Kaggle/config/algos.yml | 291 -- alphapy/examples/Kaggle/config/model.yml | 107 - .../Kaggle/input/gender_submission.csv | 419 -- alphapy/examples/Kaggle/input/test.csv | 419 -- alphapy/examples/Kaggle/input/train.csv | 892 ---- alphapy/examples/NCAAB/config/algos.yml | 291 -- alphapy/examples/NCAAB/config/model.yml | 112 - alphapy/examples/NCAAB/config/sport.yml | 7 - .../NCAAB/data/ncaab_game_scores_1g.csv | 4020 ----------------- .../Trading Model/A Trading Model.ipynb | 328 -- .../examples/Trading Model/config/algos.yml | 291 -- .../examples/Trading Model/config/market.yml | 141 - .../examples/Trading Model/config/model.yml | 124 - .../Trading System/A Trading System.ipynb | 183 - .../examples/Trading System/config/algos.yml | 291 -- .../examples/Trading System/config/market.yml | 31 - .../examples/Trading System/config/model.yml | 107 - docs/.DS_Store | Bin 0 -> 6148 bytes 20 files changed, 8054 deletions(-) create mode 100644 .DS_Store create mode 100644 alphapy/.DS_Store delete mode 100644 alphapy/examples/Kaggle/config/algos.yml delete mode 100644 alphapy/examples/Kaggle/config/model.yml delete mode 100644 alphapy/examples/Kaggle/input/gender_submission.csv delete mode 100644 alphapy/examples/Kaggle/input/test.csv delete mode 100644 alphapy/examples/Kaggle/input/train.csv delete mode 100644 alphapy/examples/NCAAB/config/algos.yml delete mode 100644 alphapy/examples/NCAAB/config/model.yml delete mode 100644 alphapy/examples/NCAAB/config/sport.yml delete mode 100644 alphapy/examples/NCAAB/data/ncaab_game_scores_1g.csv delete mode 100644 alphapy/examples/Trading Model/A Trading Model.ipynb delete mode 100644 alphapy/examples/Trading Model/config/algos.yml delete mode 100644 alphapy/examples/Trading Model/config/market.yml delete mode 100644 alphapy/examples/Trading Model/config/model.yml delete mode 100644 alphapy/examples/Trading System/A Trading System.ipynb delete mode 100644 alphapy/examples/Trading System/config/algos.yml delete mode 100644 alphapy/examples/Trading System/config/market.yml delete mode 100644 alphapy/examples/Trading System/config/model.yml create mode 100644 docs/.DS_Store diff --git a/.DS_Store b/.DS_Store new file mode 100644 index 0000000000000000000000000000000000000000..b1250337eed5cb704131ca3ebb76b44ce7ee6207 GIT binary patch literal 6148 zcmeHKu}T9$5S@)tgQ8fdh*%tcfZ!K+N^C4d@B=i-Ng`gjKulqGA7CLif*@)o{(_ZY zAvU%)+S}Op1-{wcB)hr9NieT_{f%08ZOAQ#NTtzsU8+%w%5HU{iOp<;%i;9m@%_qh=$WUUbeRl}a`_N# z89pV7sR1@6^hZ&%5|@)C#?l6=p39>;{5hG8aH*axyguKwse1FcR0l_SR7dVn3_n>! z300`+F`2$xaeXd_m-(l*T%BCEdOyEcclL_wj^H(^LyIcy_0(1eZ@qrE{>FKyzBh-< zVNi_6L!B@Wu^&)?Uu;-m+@SQLfG8jebQIwAAwXjcEv5$b(t%DN0f2c7Yr{34B{(Or z7+OpX!UIz#6=+hGJz^-6j`6_8g%(qTCY_W$d?;I4*%OM=)iHmd!%2k(r56Q60bhY3 zb34WR|N7wbzn>&`qJSvyuM|-E;&yQjk7W1O%){|s8=&o?v2k2#P^X}?+p)gjt#}Si a8+-v@07HwZL5#rUkARjzI#J+P75D;9{j^yC literal 0 HcmV?d00001 diff --git a/alphapy/.DS_Store b/alphapy/.DS_Store new file mode 100644 index 0000000000000000000000000000000000000000..5008ddfcf53c02e82d7eee2e57c38e5672ef89f6 GIT binary patch literal 6148 zcmeH~Jr2S!425mzP>H1@V-^m;4Wg<&0T*E43hX&L&p$$qDprKhvt+--jT7}7np#A3 zem<@ulZcFPQ@L2!n>{z**++&mCkOWA81W14cNZlEfg7;MkzE(HCqgga^y>{tEnwC%0;vJ&^%eQ zLs35+`xjp>T0At different thresholds, how effective is the model at predicting
larger-than-average range days?" - ] - }, - { - "cell_type": "code", - "execution_count": 1, - "metadata": { - "collapsed": true - }, - "outputs": [], - "source": [ - "%matplotlib inline" - ] - }, - { - "cell_type": "code", - "execution_count": 2, - "metadata": { - "collapsed": true - }, - "outputs": [], - "source": [ - "import numpy as np\n", - "import pandas as pd" - ] - }, - { - "cell_type": "code", - "execution_count": 3, - "metadata": {}, - "outputs": [ - { - "data": { - "text/plain": [ - "'/Users/markconway/Projects/AlphaPy/alphapy/examples/Trading Model'" - ] - }, - "execution_count": 3, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "pwd" - ] - }, - { - "cell_type": "code", - "execution_count": 4, - "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "/Users/markconway/Projects/AlphaPy/alphapy/examples/Trading Model/output\n" - ] - } - ], - "source": [ - "cd output" - ] - }, - { - "cell_type": "code", - "execution_count": 5, - "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "predictions_20170425.csv rankings_20170425.csv\r\n", - "probabilities_20170425.csv\r\n" - ] - } - ], - "source": [ - "ls" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "This file contains the ranked predictions of the test set." - ] - }, - { - "cell_type": "code", - "execution_count": 6, - "metadata": { - "collapsed": true - }, - "outputs": [], - "source": [ - "ranking_frame = pd.read_csv('rankings_20170425.csv')" - ] - }, - { - "cell_type": "code", - "execution_count": 7, - "metadata": {}, - "outputs": [ - { - "data": { - "text/plain": [ - "Index(['date', 'tag', 'open', 'high', 'low', 'close', 'volume', 'adjclose',\n", - " 'cma_3', 'abovema_3',\n", - " ...\n", - " 'rmax_5', 'wr_5', 'rmax_6', 'wr_6', 'rmax_7', 'wr_7', 'rmax_10',\n", - " 'wr_10', 'prediction', 'probability'],\n", - " dtype='object', length=180)" - ] - }, - "execution_count": 7, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "ranking_frame.columns" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "The probabilities are in descending order. Observe the greater number of True values at the top of the rankings versus the bottom." - ] - }, - { - "cell_type": "code", - "execution_count": 8, - "metadata": {}, - "outputs": [ - { - "data": { - "text/plain": [ - "0 True\n", - "1 True\n", - "2 True\n", - "3 True\n", - "4 False\n", - "5 True\n", - "6 True\n", - "7 True\n", - "8 True\n", - "9 False\n", - "10 True\n", - "11 False\n", - "12 False\n", - "13 False\n", - "14 False\n", - "15 True\n", - "16 True\n", - "17 False\n", - "18 True\n", - "19 True\n", - "Name: rrover, dtype: bool" - ] - }, - "execution_count": 8, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "ranking_frame.rrover.head(20)" - ] - }, - { - "cell_type": "code", - "execution_count": 9, - "metadata": {}, - "outputs": [ - { - "data": { - "text/plain": [ - "436 False\n", - "437 False\n", - "438 False\n", - "439 False\n", - "440 False\n", - "441 False\n", - "442 False\n", - "443 False\n", - "444 True\n", - "445 False\n", - "446 False\n", - "447 True\n", - "448 False\n", - "449 False\n", - "450 False\n", - "451 False\n", - "452 False\n", - "453 False\n", - "454 False\n", - "455 False\n", - "Name: rrover, dtype: bool" - ] - }, - "execution_count": 9, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "ranking_frame.rrover.tail(20)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Let's plot the True/False ratios for each probability decile. These ratios should roughly reflect the trend in the calibration plot." - ] - }, - { - "cell_type": "code", - "execution_count": 10, - "metadata": { - "collapsed": true - }, - "outputs": [], - "source": [ - "ranking_frame['bins'] = pd.qcut(ranking_frame.probability, 10, labels=False)" - ] - }, - { - "cell_type": "code", - "execution_count": 11, - "metadata": { - "collapsed": true - }, - "outputs": [], - "source": [ - "grouped = ranking_frame.groupby('bins')" - ] - }, - { - "cell_type": "code", - "execution_count": 12, - "metadata": { - "collapsed": true - }, - "outputs": [], - "source": [ - "def get_ratio(series):\n", - " ratio = series.value_counts()[1] / series.size\n", - " return ratio" - ] - }, - { - "cell_type": "code", - "execution_count": 13, - "metadata": {}, - "outputs": [ - { - "data": { - "text/plain": [ - "" - ] - }, - "execution_count": 13, - "metadata": {}, - "output_type": "execute_result" - }, - { - "data": { - "image/png": "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\n", - "text/plain": [ - "" - ] - }, - "metadata": {}, - "output_type": "display_data" - } - ], - "source": [ - "grouped['rrover'].apply(get_ratio).plot(kind='bar')" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "#### We conclude that the model does have some value, especially with more training data.

1. For high probabilities, we could deploy a breakout or trend system.

2. For low probabilities, we could use a counter-trend system.

3. Mid-range probabilities have no predictive power in this model." - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": { - "collapsed": true - }, - "outputs": [], - "source": [] - } - ], - "metadata": { - "kernelspec": { - "display_name": "Python 3", - "language": "python", - "name": "python3" - }, - "language_info": { - "codemirror_mode": { - "name": "ipython", - "version": 3 - }, - "file_extension": ".py", - "mimetype": "text/x-python", - "name": "python", - "nbconvert_exporter": "python", - "pygments_lexer": "ipython3", - "version": "3.6.0" - } - }, - "nbformat": 4, - "nbformat_minor": 2 -} diff --git a/alphapy/examples/Trading Model/config/algos.yml b/alphapy/examples/Trading Model/config/algos.yml deleted file mode 100644 index 3850f65..0000000 --- a/alphapy/examples/Trading Model/config/algos.yml +++ /dev/null @@ -1,291 +0,0 @@ -# -# Algorithms -# - -AB: - # AdaBoost - model_type : classification - params : {"n_estimators" : n_estimators, - "random_state" : seed} - grid : {"n_estimators" : [10, 50, 100, 150, 200], - "learning_rate" : [0.2, 0.5, 0.7, 1.0, 1.5, 2.0], - "algorithm" : ['SAMME', 'SAMME.R']} - -CATB: - # CatBoost Binary - model_type : classification - params : {"iterations" : n_estimators, - "random_seed" : seed, - "thread_count" : n_jobs, - "verbose" : verbosity} - grid : {} - -CATBR: - # CatBoost Regression - model_type : regression - params : {"iterations" : n_estimators, - "random_seed" : seed, - "thread_count" : n_jobs, - "verbose" : verbosity} - grid : {} - -GB: - # Gradient Boosting - model_type : classification - params : {"n_estimators" : n_estimators, - "max_depth" : 3, - "random_state" : seed, - "verbose" : verbosity} - grid : {"loss" : ['deviance', 'exponential'], - "learning_rate" : [0.05, 0.1, 0.15], - "n_estimators" : [50, 100, 200], - "max_depth" : [3, 5, 10], - "min_samples_split" : [2, 3], - "min_samples_leaf" : [1, 2]} - -GBR: - # Gradient Boosting Regression - model_type : regression - params : {"n_estimators" : n_estimators, - "random_state" : seed, - "verbose" : verbosity} - grid : {} - -KERASC: - # Keras Classification - model_type : classification - layers : ["Dense(12, activation='relu')", - "Dense(1, activation='sigmoid')"] - compiler : {"optimizer" : 'rmsprop', - "loss" : 'binary_crossentropy', - "metrics" : 'accuracy'} - params : {"epochs" : 50, - "batch_size" : 10, - "verbose" : verbosity} - grid : {} - -KERASR: - # Keras Regression - model_type : regression - layers : ["Dense(10, activation='relu')", - "Dense(1)"] - compiler : {"optimizer" : 'rmsprop', - "loss" : 'mse'} - params : {"epochs" : 50, - "batch_size" : 10, - "verbose" : verbosity} - grid : {} - -KNN: - # K-Nearest Neighbors - model_type : classification - params : {"n_jobs" : n_jobs} - grid : {"n_neighbors" : [3, 5, 7, 10], - "weights" : ['uniform', 'distance'], - "algorithm" : ['ball_tree', 'kd_tree', 'brute', 'auto'], - "leaf_size" : [10, 20, 30, 40, 50]} - -KNR: - # K-Nearest Neighbor Regression - model_type : regression - params : {"n_jobs" : n_jobs} - grid : {} - -LGB: - # LightGBM Binary - model_type : classification - params : {"n_estimators" : n_estimators, - "random_state" : seed, - "n_jobs" : n_jobs, - "silent" : verbosity} - grid : {} - -LGBR: - # LightGBM Regression - model_type : regression - params : {"n_estimators" : n_estimators, - "random_state" : seed, - "n_jobs" : n_jobs, - "silent" : verbosity} - grid : {} - -LOGR: - # Logistic Regression - model_type : classification - params : {"random_state" : seed, - "n_jobs" : n_jobs, - "verbose" : verbosity} - grid : {"penalty" : ['l2'], - "C" : [0.00001, 0.0001, 0.001, 0.01, 0.1, 1, 10, 100, 1000, 1e4, 1e5, 1e6, 1e7], - "fit_intercept" : [True, False], - "solver" : ['newton-cg', 'lbfgs', 'liblinear', 'sag']} - -LR: - # Linear Regression - model_type : regression - params : {"n_jobs" : n_jobs} - grid : {"fit_intercept" : [True, False], - "normalize" : [True, False], - "copy_X" : [True, False]} - -LSVC: - # Linear Support Vector Classification - model_type : classification - params : {"C" : 0.01, - "max_iter" : 2000, - "penalty" : 'l1', - "dual" : False, - "random_state" : seed, - "verbose" : verbosity} - grid : {"C" : np.logspace(-2, 10, 13), - "penalty" : ['l1', 'l2'], - "dual" : [True, False], - "tol" : [0.0005, 0.001, 0.005], - "max_iter" : [500, 1000, 2000]} - -LSVM: - # Linear Support Vector Machine - model_type : classification - params : {"kernel" : 'linear', - "probability" : True, - "random_state" : seed, - "verbose" : verbosity} - grid : {"C" : np.logspace(-2, 10, 13), - "gamma" : np.logspace(-9, 3, 13), - "shrinking" : [True, False], - "tol" : [0.0005, 0.001, 0.005], - "decision_function_shape" : ['ovo', 'ovr']} - -NB: - # Naive Bayes - model_type : classification - params : {} - grid : {"alpha" : [0.01, 0.1, 0.2, 0.3, 0.4, 0.5, 1.0, 2.0, 5.0, 10.0], - "fit_prior" : [True, False]} - -RBF: - # Radial Basis Function - model_type : classification - params : {"kernel" : 'rbf', - "probability" : True, - "random_state" : seed, - "verbose" : verbosity} - grid : {"C" : np.logspace(-2, 10, 13), - "gamma" : np.logspace(-9, 3, 13), - "shrinking" : [True, False], - "tol" : [0.0005, 0.001, 0.005], - "decision_function_shape" : ['ovo', 'ovr']} - -RF: - # Random Forest - model_type : classification - params : {"n_estimators" : n_estimators, - "max_depth" : 10, - "min_samples_split" : 5, - "min_samples_leaf" : 3, - "bootstrap" : True, - "criterion" : 'entropy', - "random_state" : seed, - "n_jobs" : n_jobs, - "verbose" : verbosity} - grid : {"n_estimators" : [21, 51, 101, 201, 501], - "max_depth" : [5, 7, 10, 20], - "min_samples_split" : [2, 3, 5, 10], - "min_samples_leaf" : [1, 2, 3], - "bootstrap" : [True, False], - "criterion" : ['gini', 'entropy']} - -RFR: - # Random Forest Regression - model_type : regression - params : {"n_estimators" : n_estimators, - "random_state" : seed, - "n_jobs" : n_jobs, - "verbose" : verbosity} - grid : {} - -SVM: - # Support Vector Machine - model_type : classification - params : {"probability" : True, - "random_state" : seed, - "verbose" : verbosity} - grid : {"C" : np.logspace(-2, 10, 13), - "gamma" : np.logspace(-9, 3, 13), - "shrinking" : [True, False], - "tol" : [0.0005, 0.001, 0.005], - "decision_function_shape" : ['ovo', 'ovr']} - -XGB: - # XGBoost Binary - model_type : classification - params : {"objective" : 'binary:logistic', - "n_estimators" : 300, - "seed" : seed, - "max_depth" : 3, - "learning_rate" : 0.05, - "min_child_weight" : 1.0, - "subsample" : 1.0, - "colsample_bytree" : 1.0, - "nthread" : n_jobs, - "verbosity" : verbosity} - grid : {"n_estimators" : [21, 51, 101, 201, 501], - "max_depth" : [5, 6, 7, 8, 9, 10, 12, 15, 20], - "learning_rate" : [0.01, 0.02, 0.05, 0.1, 0.2], - "min_child_weight" : [1.0, 1.1], - "subsample" : [0.5, 0.6, 0.7, 0.8, 0.9, 1.0], - "colsample_bytree" : [0.5, 0.6, 0.7, 0.8, 0.9, 1.0]} - -XGBM: - # XGBoost Multiclass - model_type : multiclass - params : {"objective" : 'multi:softmax', - "n_estimators" : n_estimators, - "seed" : seed, - "max_depth" : 10, - "learning_rate" : 0.1, - "min_child_weight" : 1.1, - "subsample" : 0.9, - "colsample_bytree" : 0.9, - "nthread" : n_jobs, - "verbosity" : verbosity} - grid : {} - -XGBR: - # XGBoost Regression - model_type : regression - params : {"objective" : 'reg:linear', - "n_estimators" : n_estimators, - "max_depth" : 10, - "learning_rate" : 0.1, - "min_child_weight" : 1.1, - "subsample" : 0.9, - "colsample_bytree" : 0.9, - "seed" : seed, - "nthread" : n_jobs, - "verbosity" : verbosity} - grid : {} - -XT: - # Extra Trees - model_type : classification - params : {"n_estimators" : n_estimators, - "random_state" : seed, - "n_jobs" : n_jobs, - "verbose" : verbosity} - grid : {"n_estimators" : [21, 51, 101, 201, 501, 1001, 2001], - "max_features" : ['auto', 'sqrt', 'log2'], - "max_depth" : [3, 5, 7, 10, 20, 30], - "min_samples_split" : [2, 3], - "min_samples_leaf" : [1, 2], - "bootstrap" : [True, False], - "warm_start" : [True, False]} - -XTR: - # Extra Trees Regression - model_type : regression - params : {"n_estimators" : n_estimators, - "random_state" : seed, - "n_jobs" : n_jobs, - "verbose" : verbosity} - grid : {} diff --git a/alphapy/examples/Trading Model/config/market.yml b/alphapy/examples/Trading Model/config/market.yml deleted file mode 100644 index 06b81a7..0000000 --- a/alphapy/examples/Trading Model/config/market.yml +++ /dev/null @@ -1,141 +0,0 @@ -market: - create_model : True - data_fractal : 1d - data_history : 500 - forecast_period : 1 - fractal : 1d - lag_period : 1 - leaders : ['gap', 'gapbadown', 'gapbaup', 'gapdown', 'gapup'] - predict_history : 100 - schema : yahoo - subschema : - api_key_name : - api_key : - subject : stock - target_group : test - -groups: - all : ['aaoi', 'aapl', 'acia', 'adbe', 'adi', 'adp', 'agn', 'aig', 'akam', - 'algn', 'alk', 'alxn', 'amat', 'amba', 'amd', 'amgn', 'amt', 'amzn', - 'antm', 'arch', 'asml', 'athn', 'atvi', 'auph', 'avgo', 'axp', 'ayx', - 'azo', 'ba', 'baba', 'bac', 'bby', 'bidu', 'biib', 'brcd', 'bvsn', - 'bwld', 'c', 'cacc', 'cara', 'casy', 'cat', 'cde', 'celg', 'cern', - 'chkp', 'chtr', 'clvs', 'cme', 'cmg', 'cof', 'cohr', 'comm', 'cost', - 'cpk', 'crm', 'crus', 'csco', 'ctsh', 'ctxs', 'csx', 'cvs', 'cybr', - 'data', 'ddd', 'deck', 'dgaz', 'dia', 'dis', 'dish', 'dnkn', 'dpz', - 'drys', 'dust', 'ea', 'ebay', 'edc', 'edz', 'eem', 'elli', 'eog', - 'esrx', 'etrm', 'ewh', 'ewt', 'expe', 'fang', 'fas', 'faz', 'fb', - 'fcx', 'fdx', 'ffiv', 'fit', 'five', 'fnsr', 'fslr', 'ftnt', 'gddy', - 'gdx', 'gdxj', 'ge', 'gild', 'gld', 'glw', 'gm', 'googl', 'gpro', - 'grub', 'gs', 'gwph', 'hal', 'has', 'hd', 'hdp', 'hlf', 'hog', 'hum', - 'ibb', 'ibm', 'ice', 'idxx', 'ilmn', 'ilmn', 'incy', 'intc', 'intu', - 'ip', 'isrg', 'iwm', 'ivv', 'iwf', 'iwm', 'jack', 'jcp', 'jdst', 'jnj', - 'jnpr', 'jnug', 'jpm', 'kite', 'klac', 'ko', 'kss', 'labd', 'labu', - 'len', 'lite', 'lmt', 'lnkd', 'lrcx', 'lulu', 'lvs', 'mbly', 'mcd', - 'mchp', 'mdy', 'meoh', 'mnst', 'mo', 'momo', 'mon', 'mrk', 'ms', 'msft', - 'mtb', 'mu', 'nflx', 'nfx', 'nke', 'ntap', 'ntes', 'ntnx', 'nugt', - 'nvda', 'nxpi', 'nxst', 'oii', 'oled', 'orcl', 'orly', 'p', 'panw', - 'pcln', 'pg', 'pm', 'pnra', 'prgo', 'pxd', 'pypl', 'qcom', 'qqq', - 'qrvo', 'rht', 'sam', 'sbux', 'sds', 'sgen', 'shld', 'shop', 'sig', - 'sina', 'siri', 'skx', 'slb', 'slv', 'smh', 'snap', 'sncr', 'soda', - 'splk', 'spy', 'stld', 'stmp', 'stx', 'svxy', 'swks', 'symc', 't', - 'tbt', 'teva', 'tgt', 'tho', 'tlt', 'tmo', 'tna', 'tqqq', 'trip', - 'tsla', 'ttwo', 'tvix', 'twlo', 'twtr', 'tza', 'uaa', 'ugaz', 'uhs', - 'ulta', 'ulti', 'unh', 'unp', 'upro', 'uri', 'ups', 'uri', 'uthr', - 'utx', 'uvxy', 'v', 'veev', 'viav', 'vlo', 'vmc', 'vrsn', 'vrtx', 'vrx', - 'vwo', 'vxx', 'vz', 'wday', 'wdc', 'wfc', 'wfm', 'wmt', 'wynn', 'x', - 'xbi', 'xhb', 'xiv', 'xle', 'xlf', 'xlk', 'xlnx', 'xom', 'xlp', 'xlu', - 'xlv', 'xme', 'xom', 'wix', 'yelp', 'z'] - etf : ['dia', 'dust', 'edc', 'edz', 'eem', 'ewh', 'ewt', 'fas', 'faz', - 'gld', 'hyg', 'iwm', 'ivv', 'iwf', 'jnk', 'mdy', 'nugt', 'qqq', - 'sds', 'smh', 'spy', 'tbt', 'tlt', 'tna', 'tvix', 'tza', 'upro', - 'uvxy', 'vwo', 'vxx', 'xhb', 'xiv', 'xle', 'xlf', 'xlk', 'xlp', - 'xlu', 'xlv', 'xme'] - tech : ['aapl', 'adbe', 'amat', 'amgn', 'amzn', 'avgo', 'baba', 'bidu', - 'brcd', 'csco', 'ddd', 'emc', 'expe', 'fb', 'fit', 'fslr', 'goog', - 'intc', 'isrg', 'lnkd', 'msft', 'nflx', 'nvda', 'pcln', 'qcom', - 'qqq', 'tsla', 'twtr'] - test : ['aapl', 'amzn', 'goog', 'fb', 'nvda', 'tsla'] - -features: ['abovema_3', 'abovema_5', 'abovema_10', 'abovema_20', 'abovema_50', - 'adx', 'atr', 'bigdown', 'bigup', 'diminus', 'diplus', 'doji', - 'gap', 'gapbadown', 'gapbaup', 'gapdown', 'gapup', - 'hc', 'hh', 'ho', 'hl', 'lc', 'lh', 'll', 'lo', 'hookdown', 'hookup', - 'inside', 'outside', 'madelta_3', 'madelta_5', 'madelta_7', 'madelta_10', - 'madelta_12', 'madelta_15', 'madelta_18', 'madelta_20', 'madelta', - 'net', 'netdown', 'netup', 'nr_3', 'nr_4', 'nr_5', 'nr_7', 'nr_8', - 'nr_10', 'nr_18', 'roi', 'roi_2', 'roi_3', 'roi_4', 'roi_5', 'roi_10', - 'roi_20', 'rr_1_4', 'rr_1_7', 'rr_1_10', 'rr_2_5', 'rr_2_7', 'rr_2_10', - 'rr_3_8', 'rr_3_14', 'rr_4_10', 'rr_4_20', 'rr_5_10', 'rr_5_20', - 'rr_5_30', 'rr_6_14', 'rr_6_25', 'rr_7_14', 'rr_7_35', 'rr_8_22', - 'rrhigh', 'rrlow', 'rrover', 'rrunder', 'rsi_3', 'rsi_4', 'rsi_5', - 'rsi_6', 'rsi_8', 'rsi_10', 'rsi_14', 'sep_3_3', 'sep_5_5', 'sep_8_8', - 'sep_10_10', 'sep_14_14', 'sep_21_21', 'sep_30_30', 'sep_40_40', - 'sephigh', 'seplow', 'trend', 'vma', 'vmover', 'vmratio', 'vmunder', - 'volatility_3', 'volatility_5', 'volatility', 'volatility_20', - 'wr_2', 'wr_3', 'wr', 'wr_5', 'wr_6', 'wr_7', 'wr_10'] - -aliases: - atr : 'ma_truerange' - aver : 'ma_hlrange' - cma : 'ma_close' - cmax : 'highest_close' - cmin : 'lowest_close' - hc : 'higher_close' - hh : 'higher_high' - hl : 'higher_low' - ho : 'higher_open' - hmax : 'highest_high' - hmin : 'lowest_high' - lc : 'lower_close' - lh : 'lower_high' - ll : 'lower_low' - lo : 'lower_open' - lmax : 'highest_low' - lmin : 'lowest_low' - net : 'net_close' - netdown : 'down_net' - netup : 'up_net' - omax : 'highest_open' - omin : 'lowest_open' - rmax : 'highest_hlrange' - rmin : 'lowest_hlrange' - rr : 'maratio_hlrange' - rixc : 'rindex_close_high_low' - rixo : 'rindex_open_high_low' - roi : 'netreturn_close' - rsi : 'rsi_close' - sepma : 'ma_sep' - vma : 'ma_volume' - vmratio : 'maratio_volume' - upmove : 'net_high' - -variables: - abovema : 'close > cma_50' - belowma : 'close < cma_50' - bigup : 'rrover & sephigh & netup' - bigdown : 'rrover & sephigh & netdown' - doji : 'sepdoji & rrunder' - hookdown : 'open > high[1] & close < close[1]' - hookup : 'open < low[1] & close > close[1]' - inside : 'low > low[1] & high < high[1]' - madelta : '(close - cma_50) / atr_10' - nr : 'hlrange == rmin_4' - outside : 'low < low[1] & high > high[1]' - roihigh : 'roi_5 >= 5' - roilow : 'roi_5 < -5' - roiminus : 'roi_5 < 0' - roiplus : 'roi_5 > 0' - rrhigh : 'rr_1_10 >= 1.2' - rrlow : 'rr_1_10 <= 0.8' - rrover : 'rr_1_10 >= 1.0' - rrunder : 'rr_1_10 < 1.0' - sep : 'rixc_1 - rixo_1' - sepdoji : 'abs(sep) <= 15' - sephigh : 'abs(sep_1_1) >= 70' - seplow : 'abs(sep_1_1) <= 30' - trend : 'rrover & sephigh' - vmover : 'vmratio >= 1' - vmunder : 'vmratio < 1' - volatility : 'atr_10 / close' - wr : 'hlrange == rmax_4' diff --git a/alphapy/examples/Trading Model/config/model.yml b/alphapy/examples/Trading Model/config/model.yml deleted file mode 100644 index 4f11f4b..0000000 --- a/alphapy/examples/Trading Model/config/model.yml +++ /dev/null @@ -1,124 +0,0 @@ -project: - directory : . - file_extension : csv - submission_file : - submit_probas : False - -data: - drop : ['date', 'tag', 'open', 'high', 'low', 'close', 'volume', 'adjclose', - 'low[1]', 'high[1]', 'net', 'close[1]', 'rmin_3', 'rmin_4', 'rmin_5', - 'rmin_7', 'rmin_8', 'rmin_10', 'rmin_18', 'pval', 'mval', 'vma', - 'rmax_2', 'rmax_3', 'rmax_4', 'rmax_5', 'rmax_6', 'rmax_7', 'rmax_10'] - features : '*' - sampling : - option : True - method : under_random - ratio : 0.5 - sentinel : -1 - separator : ',' - shuffle : True - split : 0.4 - target : rrover - target_value : True - -model: - algorithms : ['RF'] - balance_classes : True - calibration : - option : False - type : isotonic - cv_folds : 3 - estimators : 501 - feature_selection : - option : True - percentage : 50 - uni_grid : [5, 10, 15, 20, 25] - score_func : f_classif - grid_search : - option : False - iterations : 100 - random : True - subsample : True - sampling_pct : 0.25 - pvalue_level : 0.01 - rfe : - option : True - step : 10 - scoring_function : 'roc_auc' - type : classification - -features: - clustering : - option : False - increment : 3 - maximum : 30 - minimum : 3 - counts : - option : False - encoding : - rounding : 3 - type : target - factors : [] - interactions : - option : True - poly_degree : 2 - sampling_pct : 5 - isomap : - option : False - components : 2 - neighbors : 5 - logtransform : - option : False - numpy : - option : False - pca : - option : False - increment : 3 - maximum : 15 - minimum : 3 - whiten : False - scaling : - option : True - type : standard - scipy : - option : False - text : - ngrams : 1 - vectorize : False - tsne : - option : False - components : 2 - learning_rate : 1000.0 - perplexity : 30.0 - variance : - option : True - threshold : 0.1 - -treatments: - doji : ['alphapy.transforms', 'runs_test', ['all'], 18] - hc : ['alphapy.transforms', 'runs_test', ['all'], 18] - hh : ['alphapy.transforms', 'runs_test', ['all'], 18] - hl : ['alphapy.transforms', 'runs_test', ['all'], 18] - ho : ['alphapy.transforms', 'runs_test', ['all'], 18] - rrhigh : ['alphapy.transforms', 'runs_test', ['all'], 18] - rrlow : ['alphapy.transforms', 'runs_test', ['all'], 18] - rrover : ['alphapy.transforms', 'runs_test', ['all'], 18] - rrunder : ['alphapy.transforms', 'runs_test', ['all'], 18] - sephigh : ['alphapy.transforms', 'runs_test', ['all'], 18] - seplow : ['alphapy.transforms', 'runs_test', ['all'], 18] - trend : ['alphapy.transforms', 'runs_test', ['all'], 18] - -pipeline: - number_jobs : -1 - seed : 10231 - verbosity : 0 - -plots: - calibration : True - confusion_matrix : True - importances : True - learning_curve : True - roc_curve : True - -xgboost: - stopping_rounds : 20 diff --git a/alphapy/examples/Trading System/A Trading System.ipynb b/alphapy/examples/Trading System/A Trading System.ipynb deleted file mode 100644 index afc90c2..0000000 --- a/alphapy/examples/Trading System/A Trading System.ipynb +++ /dev/null @@ -1,183 +0,0 @@ -{ - "cells": [ - { - "cell_type": "code", - "execution_count": 2, - "metadata": {}, - "outputs": [ - { - "ename": "ImportError", - "evalue": "cannot import name 'is_list_like'", - "output_type": "error", - "traceback": [ - "\u001b[0;31m---------------------------------------------------------------------------\u001b[0m", - "\u001b[0;31mImportError\u001b[0m Traceback (most recent call last)", - "\u001b[0;32m\u001b[0m in \u001b[0;36m\u001b[0;34m\u001b[0m\n\u001b[1;32m 1\u001b[0m \u001b[0mget_ipython\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mrun_line_magic\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m'matplotlib'\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0;34m'inline'\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 2\u001b[0m \u001b[0;32mimport\u001b[0m \u001b[0mpandas\u001b[0m \u001b[0;32mas\u001b[0m \u001b[0mpd\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m----> 3\u001b[0;31m \u001b[0;32mimport\u001b[0m \u001b[0mpyfolio\u001b[0m \u001b[0;32mas\u001b[0m \u001b[0mpf\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m", - "\u001b[0;32m/anaconda3/lib/python3.6/site-packages/pyfolio/__init__.py\u001b[0m in \u001b[0;36m\u001b[0;34m\u001b[0m\n\u001b[1;32m 1\u001b[0m \u001b[0;32mimport\u001b[0m \u001b[0mwarnings\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 2\u001b[0m \u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m----> 3\u001b[0;31m \u001b[0;32mfrom\u001b[0m \u001b[0;34m.\u001b[0m \u001b[0;32mimport\u001b[0m \u001b[0mutils\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m\u001b[1;32m 4\u001b[0m \u001b[0;32mfrom\u001b[0m \u001b[0;34m.\u001b[0m \u001b[0;32mimport\u001b[0m \u001b[0mtimeseries\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 5\u001b[0m \u001b[0;32mfrom\u001b[0m \u001b[0;34m.\u001b[0m \u001b[0;32mimport\u001b[0m \u001b[0mpos\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n", - "\u001b[0;32m/anaconda3/lib/python3.6/site-packages/pyfolio/utils.py\u001b[0m in \u001b[0;36m\u001b[0;34m\u001b[0m\n\u001b[1;32m 22\u001b[0m \u001b[0;32mfrom\u001b[0m \u001b[0mIPython\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mdisplay\u001b[0m \u001b[0;32mimport\u001b[0m \u001b[0mdisplay\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 23\u001b[0m \u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m---> 24\u001b[0;31m \u001b[0;32mimport\u001b[0m \u001b[0mempyrical\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mutils\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m\u001b[1;32m 25\u001b[0m \u001b[0;32mfrom\u001b[0m \u001b[0mos\u001b[0m \u001b[0;32mimport\u001b[0m \u001b[0menviron\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 26\u001b[0m \u001b[0;32mfrom\u001b[0m \u001b[0;34m.\u001b[0m\u001b[0mdeprecate\u001b[0m \u001b[0;32mimport\u001b[0m \u001b[0mdeprecated\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n", - "\u001b[0;32m/anaconda3/lib/python3.6/site-packages/empyrical/__init__.py\u001b[0m in \u001b[0;36m\u001b[0;34m\u001b[0m\n\u001b[1;32m 19\u001b[0m \u001b[0;32mdel\u001b[0m \u001b[0mget_versions\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 20\u001b[0m \u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m---> 21\u001b[0;31m from .stats import (\n\u001b[0m\u001b[1;32m 22\u001b[0m \u001b[0maggregate_returns\u001b[0m\u001b[0;34m,\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 23\u001b[0m \u001b[0malpha\u001b[0m\u001b[0;34m,\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n", - "\u001b[0;32m/anaconda3/lib/python3.6/site-packages/empyrical/stats.py\u001b[0m in \u001b[0;36m\u001b[0;34m\u001b[0m\n\u001b[1;32m 21\u001b[0m \u001b[0;32mfrom\u001b[0m \u001b[0msix\u001b[0m \u001b[0;32mimport\u001b[0m \u001b[0miteritems\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 22\u001b[0m \u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m---> 23\u001b[0;31m \u001b[0;32mfrom\u001b[0m \u001b[0;34m.\u001b[0m\u001b[0mutils\u001b[0m \u001b[0;32mimport\u001b[0m \u001b[0mnanmean\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mnanstd\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mnanmin\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mup\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mdown\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mroll\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mrolling_window\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m\u001b[1;32m 24\u001b[0m \u001b[0;32mfrom\u001b[0m \u001b[0;34m.\u001b[0m\u001b[0mperiods\u001b[0m \u001b[0;32mimport\u001b[0m \u001b[0mANNUALIZATION_FACTORS\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mAPPROX_BDAYS_PER_YEAR\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 25\u001b[0m \u001b[0;32mfrom\u001b[0m \u001b[0;34m.\u001b[0m\u001b[0mperiods\u001b[0m \u001b[0;32mimport\u001b[0m \u001b[0mDAILY\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mWEEKLY\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mMONTHLY\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mYEARLY\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n", - "\u001b[0;32m/anaconda3/lib/python3.6/site-packages/empyrical/utils.py\u001b[0m in \u001b[0;36m\u001b[0;34m\u001b[0m\n\u001b[1;32m 24\u001b[0m \u001b[0;32mimport\u001b[0m \u001b[0mpandas\u001b[0m \u001b[0;32mas\u001b[0m \u001b[0mpd\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 25\u001b[0m \u001b[0;32mfrom\u001b[0m \u001b[0mpandas\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mtseries\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0moffsets\u001b[0m \u001b[0;32mimport\u001b[0m \u001b[0mBDay\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m---> 26\u001b[0;31m \u001b[0;32mfrom\u001b[0m \u001b[0mpandas_datareader\u001b[0m \u001b[0;32mimport\u001b[0m \u001b[0mdata\u001b[0m \u001b[0;32mas\u001b[0m \u001b[0mweb\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m\u001b[1;32m 27\u001b[0m \u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 28\u001b[0m \u001b[0;32mtry\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n", - "\u001b[0;32m/anaconda3/lib/python3.6/site-packages/pandas_datareader/__init__.py\u001b[0m in \u001b[0;36m\u001b[0;34m\u001b[0m\n\u001b[1;32m 1\u001b[0m \u001b[0;32mfrom\u001b[0m \u001b[0;34m.\u001b[0m\u001b[0m_version\u001b[0m \u001b[0;32mimport\u001b[0m \u001b[0mget_versions\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m----> 2\u001b[0;31m from .data import (DataReader, Options, get_components_yahoo,\n\u001b[0m\u001b[1;32m 3\u001b[0m \u001b[0mget_dailysummary_iex\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mget_data_enigma\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mget_data_famafrench\u001b[0m\u001b[0;34m,\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 4\u001b[0m \u001b[0mget_data_fred\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mget_data_google\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mget_data_moex\u001b[0m\u001b[0;34m,\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 5\u001b[0m \u001b[0mget_data_morningstar\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mget_data_quandl\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mget_data_stooq\u001b[0m\u001b[0;34m,\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n", - "\u001b[0;32m/anaconda3/lib/python3.6/site-packages/pandas_datareader/data.py\u001b[0m in \u001b[0;36m\u001b[0;34m\u001b[0m\n\u001b[1;32m 12\u001b[0m \u001b[0mImmediateDeprecationError\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 13\u001b[0m \u001b[0;32mfrom\u001b[0m \u001b[0mpandas_datareader\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mfamafrench\u001b[0m \u001b[0;32mimport\u001b[0m \u001b[0mFamaFrenchReader\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m---> 14\u001b[0;31m \u001b[0;32mfrom\u001b[0m \u001b[0mpandas_datareader\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mfred\u001b[0m \u001b[0;32mimport\u001b[0m \u001b[0mFredReader\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m\u001b[1;32m 15\u001b[0m \u001b[0;32mfrom\u001b[0m \u001b[0mpandas_datareader\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mgoogle\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mdaily\u001b[0m \u001b[0;32mimport\u001b[0m \u001b[0mGoogleDailyReader\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 16\u001b[0m \u001b[0;32mfrom\u001b[0m \u001b[0mpandas_datareader\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mgoogle\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0moptions\u001b[0m \u001b[0;32mimport\u001b[0m \u001b[0mOptions\u001b[0m \u001b[0;32mas\u001b[0m \u001b[0mGoogleOptions\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n", - "\u001b[0;32m/anaconda3/lib/python3.6/site-packages/pandas_datareader/fred.py\u001b[0m in \u001b[0;36m\u001b[0;34m\u001b[0m\n\u001b[0;32m----> 1\u001b[0;31m \u001b[0;32mfrom\u001b[0m \u001b[0mpandas\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mcore\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mcommon\u001b[0m \u001b[0;32mimport\u001b[0m \u001b[0mis_list_like\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m\u001b[1;32m 2\u001b[0m \u001b[0;32mfrom\u001b[0m \u001b[0mpandas\u001b[0m \u001b[0;32mimport\u001b[0m \u001b[0mconcat\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mread_csv\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 3\u001b[0m \u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 4\u001b[0m \u001b[0;32mfrom\u001b[0m \u001b[0mpandas_datareader\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mbase\u001b[0m \u001b[0;32mimport\u001b[0m \u001b[0m_BaseReader\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 5\u001b[0m \u001b[0;34m\u001b[0m\u001b[0m\n", - "\u001b[0;31mImportError\u001b[0m: cannot import name 'is_list_like'" - ] - } - ], - "source": [ - "%matplotlib inline\n", - "import pandas as pd\n", - "import pyfolio as pf" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "pwd" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "cd systems" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "ls" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "df = pd.read_csv('faang_closer_returns_1d.csv', index_col='date', squeeze=True)\n", - "df.index = pd.to_datetime(df.index, utc=True)" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "pf.plot_monthly_returns_heatmap(df)" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "pf.plot_drawdown_periods(df, 5)" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "pf.plot_drawdown_underwater(df)" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "pf.plot_returns(df)" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "pf.plot_rolling_returns(df)" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "pf.plot_annual_returns(df)" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "pf.plot_monthly_returns_dist(df)" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "pf.show_worst_drawdown_periods(df)" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "pf.plot_return_quantiles(df)" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "pf.plot_rolling_sharpe(df)" - ] - } - ], - "metadata": { - "kernelspec": { - "display_name": "Python 3", - "language": "python", - "name": "python3" - }, - "language_info": { - "codemirror_mode": { - "name": "ipython", - "version": 3 - }, - "file_extension": ".py", - "mimetype": "text/x-python", - "name": "python", - "nbconvert_exporter": "python", - "pygments_lexer": "ipython3", - "version": "3.6.6" - } - }, - "nbformat": 4, - "nbformat_minor": 2 -} diff --git a/alphapy/examples/Trading System/config/algos.yml b/alphapy/examples/Trading System/config/algos.yml deleted file mode 100644 index 3850f65..0000000 --- a/alphapy/examples/Trading System/config/algos.yml +++ /dev/null @@ -1,291 +0,0 @@ -# -# Algorithms -# - -AB: - # AdaBoost - model_type : classification - params : {"n_estimators" : n_estimators, - "random_state" : seed} - grid : {"n_estimators" : [10, 50, 100, 150, 200], - "learning_rate" : [0.2, 0.5, 0.7, 1.0, 1.5, 2.0], - "algorithm" : ['SAMME', 'SAMME.R']} - -CATB: - # CatBoost Binary - model_type : classification - params : {"iterations" : n_estimators, - "random_seed" : seed, - "thread_count" : n_jobs, - "verbose" : verbosity} - grid : {} - -CATBR: - # CatBoost Regression - model_type : regression - params : {"iterations" : n_estimators, - "random_seed" : seed, - "thread_count" : n_jobs, - "verbose" : verbosity} - grid : {} - -GB: - # Gradient Boosting - model_type : classification - params : {"n_estimators" : n_estimators, - "max_depth" : 3, - "random_state" : seed, - "verbose" : verbosity} - grid : {"loss" : ['deviance', 'exponential'], - "learning_rate" : [0.05, 0.1, 0.15], - "n_estimators" : [50, 100, 200], - "max_depth" : [3, 5, 10], - "min_samples_split" : [2, 3], - "min_samples_leaf" : [1, 2]} - -GBR: - # Gradient Boosting Regression - model_type : regression - params : {"n_estimators" : n_estimators, - "random_state" : seed, - "verbose" : verbosity} - grid : {} - -KERASC: - # Keras Classification - model_type : classification - layers : ["Dense(12, activation='relu')", - "Dense(1, activation='sigmoid')"] - compiler : {"optimizer" : 'rmsprop', - "loss" : 'binary_crossentropy', - "metrics" : 'accuracy'} - params : {"epochs" : 50, - "batch_size" : 10, - "verbose" : verbosity} - grid : {} - -KERASR: - # Keras Regression - model_type : regression - layers : ["Dense(10, activation='relu')", - "Dense(1)"] - compiler : {"optimizer" : 'rmsprop', - "loss" : 'mse'} - params : {"epochs" : 50, - "batch_size" : 10, - "verbose" : verbosity} - grid : {} - -KNN: - # K-Nearest Neighbors - model_type : classification - params : {"n_jobs" : n_jobs} - grid : {"n_neighbors" : [3, 5, 7, 10], - "weights" : ['uniform', 'distance'], - "algorithm" : ['ball_tree', 'kd_tree', 'brute', 'auto'], - "leaf_size" : [10, 20, 30, 40, 50]} - -KNR: - # K-Nearest Neighbor Regression - model_type : regression - params : {"n_jobs" : n_jobs} - grid : {} - -LGB: - # LightGBM Binary - model_type : classification - params : {"n_estimators" : n_estimators, - "random_state" : seed, - "n_jobs" : n_jobs, - "silent" : verbosity} - grid : {} - -LGBR: - # LightGBM Regression - model_type : regression - params : {"n_estimators" : n_estimators, - "random_state" : seed, - "n_jobs" : n_jobs, - "silent" : verbosity} - grid : {} - -LOGR: - # Logistic Regression - model_type : classification - params : {"random_state" : seed, - "n_jobs" : n_jobs, - "verbose" : verbosity} - grid : {"penalty" : ['l2'], - "C" : [0.00001, 0.0001, 0.001, 0.01, 0.1, 1, 10, 100, 1000, 1e4, 1e5, 1e6, 1e7], - "fit_intercept" : [True, False], - "solver" : ['newton-cg', 'lbfgs', 'liblinear', 'sag']} - -LR: - # Linear Regression - model_type : regression - params : {"n_jobs" : n_jobs} - grid : {"fit_intercept" : [True, False], - "normalize" : [True, False], - "copy_X" : [True, False]} - -LSVC: - # Linear Support Vector Classification - model_type : classification - params : {"C" : 0.01, - "max_iter" : 2000, - "penalty" : 'l1', - "dual" : False, - "random_state" : seed, - "verbose" : verbosity} - grid : {"C" : np.logspace(-2, 10, 13), - "penalty" : ['l1', 'l2'], - "dual" : [True, False], - "tol" : [0.0005, 0.001, 0.005], - "max_iter" : [500, 1000, 2000]} - -LSVM: - # Linear Support Vector Machine - model_type : classification - params : {"kernel" : 'linear', - "probability" : True, - "random_state" : seed, - "verbose" : verbosity} - grid : {"C" : np.logspace(-2, 10, 13), - "gamma" : np.logspace(-9, 3, 13), - "shrinking" : [True, False], - "tol" : [0.0005, 0.001, 0.005], - "decision_function_shape" : ['ovo', 'ovr']} - -NB: - # Naive Bayes - model_type : classification - params : {} - grid : {"alpha" : [0.01, 0.1, 0.2, 0.3, 0.4, 0.5, 1.0, 2.0, 5.0, 10.0], - "fit_prior" : [True, False]} - -RBF: - # Radial Basis Function - model_type : classification - params : {"kernel" : 'rbf', - "probability" : True, - "random_state" : seed, - "verbose" : verbosity} - grid : {"C" : np.logspace(-2, 10, 13), - "gamma" : np.logspace(-9, 3, 13), - "shrinking" : [True, False], - "tol" : [0.0005, 0.001, 0.005], - "decision_function_shape" : ['ovo', 'ovr']} - -RF: - # Random Forest - model_type : classification - params : {"n_estimators" : n_estimators, - "max_depth" : 10, - "min_samples_split" : 5, - "min_samples_leaf" : 3, - "bootstrap" : True, - "criterion" : 'entropy', - "random_state" : seed, - "n_jobs" : n_jobs, - "verbose" : verbosity} - grid : {"n_estimators" : [21, 51, 101, 201, 501], - "max_depth" : [5, 7, 10, 20], - "min_samples_split" : [2, 3, 5, 10], - "min_samples_leaf" : [1, 2, 3], - "bootstrap" : [True, False], - "criterion" : ['gini', 'entropy']} - -RFR: - # Random Forest Regression - model_type : regression - params : {"n_estimators" : n_estimators, - "random_state" : seed, - "n_jobs" : n_jobs, - "verbose" : verbosity} - grid : {} - -SVM: - # Support Vector Machine - model_type : classification - params : {"probability" : True, - "random_state" : seed, - "verbose" : verbosity} - grid : {"C" : np.logspace(-2, 10, 13), - "gamma" : np.logspace(-9, 3, 13), - "shrinking" : [True, False], - "tol" : [0.0005, 0.001, 0.005], - "decision_function_shape" : ['ovo', 'ovr']} - -XGB: - # XGBoost Binary - model_type : classification - params : {"objective" : 'binary:logistic', - "n_estimators" : 300, - "seed" : seed, - "max_depth" : 3, - "learning_rate" : 0.05, - "min_child_weight" : 1.0, - "subsample" : 1.0, - "colsample_bytree" : 1.0, - "nthread" : n_jobs, - "verbosity" : verbosity} - grid : {"n_estimators" : [21, 51, 101, 201, 501], - "max_depth" : [5, 6, 7, 8, 9, 10, 12, 15, 20], - "learning_rate" : [0.01, 0.02, 0.05, 0.1, 0.2], - "min_child_weight" : [1.0, 1.1], - "subsample" : [0.5, 0.6, 0.7, 0.8, 0.9, 1.0], - "colsample_bytree" : [0.5, 0.6, 0.7, 0.8, 0.9, 1.0]} - -XGBM: - # XGBoost Multiclass - model_type : multiclass - params : {"objective" : 'multi:softmax', - "n_estimators" : n_estimators, - "seed" : seed, - "max_depth" : 10, - "learning_rate" : 0.1, - "min_child_weight" : 1.1, - "subsample" : 0.9, - "colsample_bytree" : 0.9, - "nthread" : n_jobs, - "verbosity" : verbosity} - grid : {} - -XGBR: - # XGBoost Regression - model_type : regression - params : {"objective" : 'reg:linear', - "n_estimators" : n_estimators, - "max_depth" : 10, - "learning_rate" : 0.1, - "min_child_weight" : 1.1, - "subsample" : 0.9, - "colsample_bytree" : 0.9, - "seed" : seed, - "nthread" : n_jobs, - "verbosity" : verbosity} - grid : {} - -XT: - # Extra Trees - model_type : classification - params : {"n_estimators" : n_estimators, - "random_state" : seed, - "n_jobs" : n_jobs, - "verbose" : verbosity} - grid : {"n_estimators" : [21, 51, 101, 201, 501, 1001, 2001], - "max_features" : ['auto', 'sqrt', 'log2'], - "max_depth" : [3, 5, 7, 10, 20, 30], - "min_samples_split" : [2, 3], - "min_samples_leaf" : [1, 2], - "bootstrap" : [True, False], - "warm_start" : [True, False]} - -XTR: - # Extra Trees Regression - model_type : regression - params : {"n_estimators" : n_estimators, - "random_state" : seed, - "n_jobs" : n_jobs, - "verbose" : verbosity} - grid : {} diff --git a/alphapy/examples/Trading System/config/market.yml b/alphapy/examples/Trading System/config/market.yml deleted file mode 100644 index f4b39d4..0000000 --- a/alphapy/examples/Trading System/config/market.yml +++ /dev/null @@ -1,31 +0,0 @@ -market: - create_model : False - data_fractal : 1d - data_history : 500 - forecast_period : 1 - fractal : 1d - lag_period : 1 - leaders : [] - predict_history : 50 - schema : yahoo - subschema : - api_key_name : - api_key : - subject : stock - target_group : faang - -system: - name : closer - holdperiod : 0 - longentry : hc - longexit : - shortentry : lc - shortexit : - scale : False - -groups: - faang : ['fb', 'aapl', 'amzn', 'nflx', 'googl'] - -aliases: - hc : 'higher_close' - lc : 'lower_close' diff --git a/alphapy/examples/Trading System/config/model.yml b/alphapy/examples/Trading System/config/model.yml deleted file mode 100644 index 615e6a1..0000000 --- a/alphapy/examples/Trading System/config/model.yml +++ /dev/null @@ -1,107 +0,0 @@ -project: - directory : . - file_extension : csv - submission_file : - submit_probas : False - -data: - drop : ['date', 'tag', 'open', 'high', 'low', 'close', 'adjclose'] - features : '*' - sampling : - option : True - method : under_random - ratio : 0.5 - sentinel : -1 - separator : ',' - shuffle : True - split : 0.4 - target : wr - target_value : True - -model: - algorithms : ['XGB'] - balance_classes : True - calibration : - option : False - type : sigmoid - cv_folds : 3 - estimators : 501 - feature_selection : - option : False - percentage : 10 - uni_grid : [5, 10, 15, 20, 25] - score_func : f_classif - grid_search : - option : False - iterations : 100 - random : True - subsample : True - sampling_pct : 0.25 - pvalue_level : 0.01 - rfe : - option : False - step : 10 - scoring_function : 'roc_auc' - type : classification - -features: - clustering : - option : False - increment : 3 - maximum : 30 - minimum : 3 - counts : - option : False - encoding : - rounding : 3 - type : target - factors : [] - interactions : - option : True - poly_degree : 2 - sampling_pct : 5 - isomap : - option : False - components : 2 - neighbors : 5 - logtransform : - option : False - numpy : - option : False - pca : - option : False - increment : 3 - maximum : 15 - minimum : 3 - whiten : False - scaling : - option : True - type : standard - scipy : - option : False - text : - ngrams : 1 - vectorize : False - tsne : - option : False - components : 2 - learning_rate : 1000.0 - perplexity : 30.0 - variance : - option : True - threshold : 0.1 - -pipeline: - number_jobs : -1 - seed : 10231 - verbosity : 1 - -plots: - calibration : True - confusion_matrix : True - importances : True - learning_curve : True - roc_curve : True - -xgboost: - stopping_rounds : 30 diff --git a/docs/.DS_Store b/docs/.DS_Store new file mode 100644 index 0000000000000000000000000000000000000000..3131e2b309385fadecb2651e4b524788f1a1519e GIT binary patch literal 6148 zcmeHK%}xR_5S}VQ1W7pRt*^jIy{%+Dc;*3+zaR-v6gM&PvL1a4Ucx8vq9lSKy`t>-H8dFw>dmehPaWyA+jX&H6UUvA=^&KpG|2D=&c!LCIE^oRILhk6wsj`HHN z9N+I399OQHW%*NVz;kELJjFJv0BSZ{sp3#tQ9u+B1r`eM_d&xMBaf*=`{_VquK>UT zhOME`zZ9IKJw_f=hnRsWmkM;L%3m>*OUJnOagoQ=p-U%acg8w?XXS4w%I=PFZNo`L z4y6?ZM1fF&ZTDE?`hS{#{|}SoNfZzT{*?l%5I5rnUdh+irI+JctKsKxHjYaj+7vXt h9NP|;;(fR^%xUfbBaf*=jKJhaz{((vD6mllJ^+j@r-A?g literal 0 HcmV?d00001 From e6419cc811c2a3abc1ad522a85a888c8ef386056 Mon Sep 17 00:00:00 2001 From: Mark Conway Date: Sat, 23 Oct 2021 03:17:16 -0400 Subject: [PATCH 105/129] Delete .DS_Store --- .DS_Store | Bin 6148 -> 0 bytes 1 file changed, 0 insertions(+), 0 deletions(-) delete mode 100644 .DS_Store diff --git a/.DS_Store b/.DS_Store deleted file mode 100644 index b1250337eed5cb704131ca3ebb76b44ce7ee6207..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 6148 zcmeHKu}T9$5S@)tgQ8fdh*%tcfZ!K+N^C4d@B=i-Ng`gjKulqGA7CLif*@)o{(_ZY zAvU%)+S}Op1-{wcB)hr9NieT_{f%08ZOAQ#NTtzsU8+%w%5HU{iOp<;%i;9m@%_qh=$WUUbeRl}a`_N# z89pV7sR1@6^hZ&%5|@)C#?l6=p39>;{5hG8aH*axyguKwse1FcR0l_SR7dVn3_n>! z300`+F`2$xaeXd_m-(l*T%BCEdOyEcclL_wj^H(^LyIcy_0(1eZ@qrE{>FKyzBh-< zVNi_6L!B@Wu^&)?Uu;-m+@SQLfG8jebQIwAAwXjcEv5$b(t%DN0f2c7Yr{34B{(Or z7+OpX!UIz#6=+hGJz^-6j`6_8g%(qTCY_W$d?;I4*%OM=)iHmd!%2k(r56Q60bhY3 zb34WR|N7wbzn>&`qJSvyuM|-E;&yQjk7W1O%){|s8=&o?v2k2#P^X}?+p)gjt#}Si a8+-v@07HwZL5#rUkARjzI#J+P75D;9{j^yC From 7ce171cb2ca527de24e86c9a43fa783e9b2e12ac Mon Sep 17 00:00:00 2001 From: Mark Conway Date: Sat, 23 Apr 2022 09:29:33 -0400 Subject: [PATCH 106/129] Update readthedocs.yml Add system_packages flag --- readthedocs.yml | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/readthedocs.yml b/readthedocs.yml index 40c0954..dac6f7f 100644 --- a/readthedocs.yml +++ b/readthedocs.yml @@ -1,2 +1,5 @@ conda: - file: environment.yml \ No newline at end of file + file: environment.yml + +python: + system_packages: true From 9bca484c35be2dd5be58a982e91dfe0bfffd04a4 Mon Sep 17 00:00:00 2001 From: Mark Conway Date: Sat, 23 Apr 2022 09:41:25 -0400 Subject: [PATCH 107/129] Update readthedocs.yml --- readthedocs.yml | 31 ++++++++++++++++++++++++++++++- 1 file changed, 30 insertions(+), 1 deletion(-) diff --git a/readthedocs.yml b/readthedocs.yml index dac6f7f..2f78734 100644 --- a/readthedocs.yml +++ b/readthedocs.yml @@ -1,5 +1,34 @@ +# .readthedocs.yaml +# Read the Docs configuration file +# See https://docs.readthedocs.io/en/stable/config-file/v2.html for details + +# Required +version: 2 + +# Set the version of Python and other tools you might need +build: + os: ubuntu-20.04 + tools: + python: "3.9" + # You can also specify other tool versions: + # nodejs: "16" + # rust: "1.55" + # golang: "1.17" + +# Build documentation in the docs/ directory with Sphinx +sphinx: + configuration: docs/conf.py + +# If using Sphinx, optionally build your docs in additional formats such as PDF +# formats: +# - pdf + conda: file: environment.yml +# Optionally declare the Python requirements required to build your docs +# install: +# - requirements: docs/requirements.txt + python: - system_packages: true + system_packages: true From 4260bc9505aa7a14c5763462411822ae68b50d43 Mon Sep 17 00:00:00 2001 From: Mark Conway Date: Sat, 23 Apr 2022 09:42:46 -0400 Subject: [PATCH 108/129] Update readthedocs.yml --- readthedocs.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/readthedocs.yml b/readthedocs.yml index 2f78734..6231e36 100644 --- a/readthedocs.yml +++ b/readthedocs.yml @@ -24,7 +24,7 @@ sphinx: # - pdf conda: - file: environment.yml + environment: environment.yml # Optionally declare the Python requirements required to build your docs # install: From 31eb34b626097b5816c0ca6574e91f83eac3bcef Mon Sep 17 00:00:00 2001 From: Mark Conway Date: Sat, 23 Apr 2022 09:47:59 -0400 Subject: [PATCH 109/129] Update readthedocs.yml --- readthedocs.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/readthedocs.yml b/readthedocs.yml index 6231e36..d1b8c08 100644 --- a/readthedocs.yml +++ b/readthedocs.yml @@ -9,7 +9,7 @@ version: 2 build: os: ubuntu-20.04 tools: - python: "3.9" + python: "mambaforge-4.10" # You can also specify other tool versions: # nodejs: "16" # rust: "1.55" From b9766ff105b2f5e72677c360f40b264cc6bb985a Mon Sep 17 00:00:00 2001 From: eromoe Date: Tue, 7 Mar 2023 10:33:33 +0800 Subject: [PATCH 110/129] fix nan feature --- alphapy/features.py | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/alphapy/features.py b/alphapy/features.py index 12bfe57..cb64957 100644 --- a/alphapy/features.py +++ b/alphapy/features.py @@ -1044,8 +1044,12 @@ def create_features(model, X, X_train, X_test, y_train): features, fnames = get_factors(model, X_train, X_test, y_train, fnum, fname, nunique, dtype, encoder, rounding, sentinel) elif dtype == 'float64' or dtype == 'int64' or dtype == 'bool': + features, fnames = get_numerical_features(fnum, fname, X, nunique, dtype, sentinel, logtransform, pvalue_level) + if nunique == 1 and np.isnan(X[fname].unique()): + # all nan, features shape is (len, 0), cause Mismatched Features and Names + features = np.zeros((X.shape[0], 1)) elif dtype == 'object': features, fnames = get_text_features(fnum, fname, X, nunique, vectorize, ngrams_max) else: From 136e3c57b2bab81735298480156cb5de11d9cf97 Mon Sep 17 00:00:00 2001 From: eromoe Date: Tue, 7 Mar 2023 10:37:10 +0800 Subject: [PATCH 111/129] fix --- alphapy/data.py | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/alphapy/data.py b/alphapy/data.py index b693710..b2f4bcc 100644 --- a/alphapy/data.py +++ b/alphapy/data.py @@ -276,8 +276,10 @@ def sample_data(model): raise ValueError("Unknown Sampling Method %s" % sampling_method) # Get the newly sampled features. - - X, y = sampler.fit_sample(X_train, y_train) + try: + X, y = sampler.fit_sample(X_train, y_train) + except AttributeError: + X, y = sampler.fit_resample(X_train, y_train) logger.info("Original Samples : %d", X_train.shape[0]) logger.info("New Samples : %d", X.shape[0]) From ee9999dce4fbe8b01042de0d0a80b99608b64af5 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 6 Jul 2023 21:56:00 +0000 Subject: [PATCH 112/129] Bump scipy from 1.4.1 to 1.10.0 Bumps [scipy](https://github.com/scipy/scipy) from 1.4.1 to 1.10.0. - [Release notes](https://github.com/scipy/scipy/releases) - [Commits](https://github.com/scipy/scipy/compare/v1.4.1...v1.10.0) --- updated-dependencies: - dependency-name: scipy dependency-type: direct:production ... Signed-off-by: dependabot[bot] --- setup.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/setup.py b/setup.py index f9e3dbd..4e121e8 100644 --- a/setup.py +++ b/setup.py @@ -38,7 +38,7 @@ 'pyfolio>=0.9', 'pyyaml>=5.0', 'scikit-learn>=0.23.1', - 'scipy==1.4.1', + 'scipy==1.10.0', 'seaborn>=0.9', 'tensorflow>=2.0', ] From 2d73ece7628c87df2faf90fe5a22aee9f5be820e Mon Sep 17 00:00:00 2001 From: Mark Conway Date: Tue, 29 Aug 2023 14:00:33 -0400 Subject: [PATCH 113/129] Update README.rst --- README.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.rst b/README.rst index 8cc049a..ae3ced9 100644 --- a/README.rst +++ b/README.rst @@ -109,4 +109,4 @@ http://alphapy.readthedocs.io/en/latest/introduction/support.html#donations .. |badge_pypi| image:: https://badge.fury.io/py/alphapy.svg .. |badge_build| image:: https://travis-ci.org/ScottfreeLLC/AlphaPy.svg?branch=master .. |badge_docs| image:: https://readthedocs.org/projects/alphapy/badge/?version=latest -.. |badge_downloads| image:: https://pepy.tech/badge/alphapy +.. |badge_downloads| image:: https://www.pepy.tech/projects/alphapy From 6214cfdd5b93dd6ea12d03c912ce3f669ae5b34f Mon Sep 17 00:00:00 2001 From: Mark Conway Date: Tue, 29 Aug 2023 14:04:10 -0400 Subject: [PATCH 114/129] Update README.rst --- README.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.rst b/README.rst index ae3ced9..b86a3d5 100644 --- a/README.rst +++ b/README.rst @@ -109,4 +109,4 @@ http://alphapy.readthedocs.io/en/latest/introduction/support.html#donations .. |badge_pypi| image:: https://badge.fury.io/py/alphapy.svg .. |badge_build| image:: https://travis-ci.org/ScottfreeLLC/AlphaPy.svg?branch=master .. |badge_docs| image:: https://readthedocs.org/projects/alphapy/badge/?version=latest -.. |badge_downloads| image:: https://www.pepy.tech/projects/alphapy +.. |badge_downloads| image:: https://static.pepy.tech/badge/alphapy From 5c8e9919a6d9b313016df2c10d5e656610d2eb1e Mon Sep 17 00:00:00 2001 From: Mark Conway Date: Wed, 30 Aug 2023 22:11:16 -0400 Subject: [PATCH 115/129] Update readthedocs.yml Remove deprecated python.system_packages option --- readthedocs.yml | 7 ------- 1 file changed, 7 deletions(-) diff --git a/readthedocs.yml b/readthedocs.yml index d1b8c08..36efe40 100644 --- a/readthedocs.yml +++ b/readthedocs.yml @@ -25,10 +25,3 @@ sphinx: conda: environment: environment.yml - -# Optionally declare the Python requirements required to build your docs -# install: -# - requirements: docs/requirements.txt - -python: - system_packages: true From e61945a89a2b62142443a082a0791caecfd630eb Mon Sep 17 00:00:00 2001 From: Mark Conway Date: Wed, 30 Aug 2023 22:18:51 -0400 Subject: [PATCH 116/129] Update README.rst --- README.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.rst b/README.rst index b86a3d5..4b3c34e 100644 --- a/README.rst +++ b/README.rst @@ -1,7 +1,7 @@ AlphaPy ======= -|badge_pypi| |badge_build| |badge_docs| |badge_downloads| +|badge_pypi| |badge_downloads| |badge_docs| |badge_build| **AlphaPy** is a machine learning framework for both speculators and data scientists. It is written in Python mainly with the ``scikit-learn`` From d86d6a20eb31715e668300b87de1c3eb13b7efec Mon Sep 17 00:00:00 2001 From: Mark Conway Date: Sun, 5 Nov 2023 13:44:10 -0500 Subject: [PATCH 117/129] Update conf.py to add dark mode --- docs/conf.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/docs/conf.py b/docs/conf.py index 5609f5a..5b53d3e 100644 --- a/docs/conf.py +++ b/docs/conf.py @@ -34,7 +34,9 @@ # ones. extensions = ['sphinx.ext.autodoc', 'sphinx.ext.napoleon', - 'sphinx.ext.mathjax'] + 'sphinx.ext.mathjax', + 'sphinx_rtd_dark_mode', + ] napoleon_google_docstring = False napoleon_use_param = False From 144e15c0a6f7338c79c51efda2d1889816014f95 Mon Sep 17 00:00:00 2001 From: Mark Conway Date: Sun, 5 Nov 2023 13:48:57 -0500 Subject: [PATCH 118/129] Update conf.py --- docs/conf.py | 1 - 1 file changed, 1 deletion(-) diff --git a/docs/conf.py b/docs/conf.py index 5b53d3e..343f144 100644 --- a/docs/conf.py +++ b/docs/conf.py @@ -35,7 +35,6 @@ extensions = ['sphinx.ext.autodoc', 'sphinx.ext.napoleon', 'sphinx.ext.mathjax', - 'sphinx_rtd_dark_mode', ] napoleon_google_docstring = False From f920bc8bcb9a1a782e4e335f2c9d60ae20b0b6be Mon Sep 17 00:00:00 2001 From: Mark Conway Date: Wed, 13 Dec 2023 12:51:57 -0500 Subject: [PATCH 119/129] Update conf.py --- docs/conf.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/docs/conf.py b/docs/conf.py index 343f144..5609f5a 100644 --- a/docs/conf.py +++ b/docs/conf.py @@ -34,8 +34,7 @@ # ones. extensions = ['sphinx.ext.autodoc', 'sphinx.ext.napoleon', - 'sphinx.ext.mathjax', - ] + 'sphinx.ext.mathjax'] napoleon_google_docstring = False napoleon_use_param = False From 0d88d6af8a3ef5419cb8ddb6ba6cb691cc09801b Mon Sep 17 00:00:00 2001 From: Mark Conway Date: Wed, 13 Dec 2023 13:07:35 -0500 Subject: [PATCH 120/129] Update conf.py --- docs/conf.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/conf.py b/docs/conf.py index 5609f5a..0839c4e 100644 --- a/docs/conf.py +++ b/docs/conf.py @@ -71,7 +71,7 @@ # # This is also used if you do content translation via gettext catalogs. # Usually you set "language" from the command line for these cases. -language = None +language = 'en' # List of patterns, relative to source directory, that match files and # directories to ignore when looking for source files. From 0baea6da2962645a43025842afde8659fc9675fa Mon Sep 17 00:00:00 2001 From: Mark Conway Date: Wed, 13 Dec 2023 13:12:48 -0500 Subject: [PATCH 121/129] Update conf.py --- docs/conf.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/docs/conf.py b/docs/conf.py index 0839c4e..7531cdf 100644 --- a/docs/conf.py +++ b/docs/conf.py @@ -34,7 +34,8 @@ # ones. extensions = ['sphinx.ext.autodoc', 'sphinx.ext.napoleon', - 'sphinx.ext.mathjax'] + 'sphinx.ext.mathjax', + 'sphinx_rtd_theme'] napoleon_google_docstring = False napoleon_use_param = False From 56ffebe117d9be20a58016c021d408dc798408ce Mon Sep 17 00:00:00 2001 From: Mark Conway Date: Wed, 13 Dec 2023 13:24:44 -0500 Subject: [PATCH 122/129] Update environment.yml --- environment.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/environment.yml b/environment.yml index 226952b..c79ac95 100644 --- a/environment.yml +++ b/environment.yml @@ -22,3 +22,4 @@ dependencies: - imbalanced-learn>=0.5 - pandas-datareader>=0.8 - pyfolio>=0.9 + - sphinx_rtd_theme>=2.0 From 9b4bf1db7d7a1b9c32b53f73ccb5d105cadd7f8f Mon Sep 17 00:00:00 2001 From: Mark Conway Date: Wed, 13 Dec 2023 13:37:54 -0500 Subject: [PATCH 123/129] Update README.rst --- README.rst | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/README.rst b/README.rst index 4b3c34e..180feff 100644 --- a/README.rst +++ b/README.rst @@ -1,7 +1,7 @@ AlphaPy ======= -|badge_pypi| |badge_downloads| |badge_docs| |badge_build| +|badge_pypi| |badge_downloads| |badge_docs| **AlphaPy** is a machine learning framework for both speculators and data scientists. It is written in Python mainly with the ``scikit-learn`` @@ -107,6 +107,5 @@ http://alphapy.readthedocs.io/en/latest/introduction/support.html#donations .. |badge_pypi| image:: https://badge.fury.io/py/alphapy.svg -.. |badge_build| image:: https://travis-ci.org/ScottfreeLLC/AlphaPy.svg?branch=master .. |badge_docs| image:: https://readthedocs.org/projects/alphapy/badge/?version=latest .. |badge_downloads| image:: https://static.pepy.tech/badge/alphapy From e57b7a3dc8d53b62d2b9954bc1a335e8864b59b5 Mon Sep 17 00:00:00 2001 From: Mark Conway Date: Wed, 13 Dec 2023 13:39:21 -0500 Subject: [PATCH 124/129] Update environment.yml --- environment.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/environment.yml b/environment.yml index c79ac95..d0e4579 100644 --- a/environment.yml +++ b/environment.yml @@ -22,4 +22,5 @@ dependencies: - imbalanced-learn>=0.5 - pandas-datareader>=0.8 - pyfolio>=0.9 + - sphinx-rtd-dark-mode>=1.3.0 - sphinx_rtd_theme>=2.0 From 367d19490371eab2f6810feaacf265aa9876c2d3 Mon Sep 17 00:00:00 2001 From: Mark Conway Date: Wed, 13 Dec 2023 13:40:36 -0500 Subject: [PATCH 125/129] Update conf.py for dark mode --- docs/conf.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/docs/conf.py b/docs/conf.py index 7531cdf..0aae287 100644 --- a/docs/conf.py +++ b/docs/conf.py @@ -35,7 +35,8 @@ extensions = ['sphinx.ext.autodoc', 'sphinx.ext.napoleon', 'sphinx.ext.mathjax', - 'sphinx_rtd_theme'] + 'sphinx_rtd_theme', + 'sphinx_rtd_dark_mode'] napoleon_google_docstring = False napoleon_use_param = False From e496d2bcf890e6f3737764f607e31bbfc9ba61a9 Mon Sep 17 00:00:00 2001 From: Mark Conway Date: Sat, 10 Feb 2024 11:39:03 -0500 Subject: [PATCH 126/129] Update README.rst --- README.rst | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/README.rst b/README.rst index 180feff..9ad7b3c 100644 --- a/README.rst +++ b/README.rst @@ -26,6 +26,11 @@ Documentation http://alphapy.readthedocs.io/en/latest/ +Coming Soon: AlphaPy Pro +------------------------ + +https://www.scottfreellc.com/alphapy-pro + Installation ------------ From 2f98bd497220bdead0407eed98b4086c1cba770d Mon Sep 17 00:00:00 2001 From: Mark Conway Date: Sat, 10 Feb 2024 11:41:20 -0500 Subject: [PATCH 127/129] Update README.rst --- README.rst | 17 ++++++++++++----- 1 file changed, 12 insertions(+), 5 deletions(-) diff --git a/README.rst b/README.rst index 9ad7b3c..6e7f718 100644 --- a/README.rst +++ b/README.rst @@ -21,16 +21,16 @@ some of the things you can do with AlphaPy: :alt: AlphaPy Model Pipeline :align: center +AlphaPy Pro: Coming Soon +------------------------ + +https://www.scottfreellc.com/alphapy-pro + Documentation ------------- http://alphapy.readthedocs.io/en/latest/ -Coming Soon: AlphaPy Pro ------------------------- - -https://www.scottfreellc.com/alphapy-pro - Installation ------------ @@ -92,6 +92,13 @@ SportFlow :alt: SportFlow :align: center +GamePT +------ + +You can find an implementation of MarketFlow here: + +https://www.scottfreellc.com/gamept + Support ------- From 25086cf676eb5168979b7701e25c5c80ddab8e71 Mon Sep 17 00:00:00 2001 From: Mark Conway Date: Sun, 15 Dec 2024 12:30:23 -0500 Subject: [PATCH 128/129] Copyright Update --- docs/conf.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/conf.py b/docs/conf.py index 0aae287..acf340d 100644 --- a/docs/conf.py +++ b/docs/conf.py @@ -56,7 +56,7 @@ # General information about the project. project = 'AlphaPy' -copyright = '2020, ScottFree Analytics LLC' +copyright = '2024, ScottFree Analytics LLC' author = 'Robert D. Scott II, Mark Conway' # The version info for the project you're documenting, acts as replacement for From f7a14b6da031e1f05693578907f82189559419dc Mon Sep 17 00:00:00 2001 From: Mark Conway Date: Sun, 24 Aug 2025 09:55:21 -0400 Subject: [PATCH 129/129] Announce AlphaPy Pro public release MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Add prominent announcement banner for AlphaPy Pro - Update "Coming Soon" section to "Now Available" - Include links to repository, documentation, and PyPI - Add installation instructions for AlphaPy Pro - Note that active development has moved to AlphaPy Pro 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude --- README.rst | 39 ++++++++++++++++++++++++++++++++++----- 1 file changed, 34 insertions(+), 5 deletions(-) diff --git a/README.rst b/README.rst index 6e7f718..112ee55 100644 --- a/README.rst +++ b/README.rst @@ -6,8 +6,18 @@ AlphaPy **AlphaPy** is a machine learning framework for both speculators and data scientists. It is written in Python mainly with the ``scikit-learn`` and ``pandas`` libraries, as well as many other helpful -packages for feature engineering and visualization. Here are just -some of the things you can do with AlphaPy: +packages for feature engineering and visualization. + +🚀 **AlphaPy Pro is Now Available!** + +**AlphaPy Pro** - the professional edition of AlphaPy - is now publicly available! +Featuring modern Python 3.12+ support, enhanced performance, and enterprise-grade capabilities. + +* **Repository**: https://github.com/ScottfreeLLC/alphapy-pro +* **Documentation**: https://scottfreellc.github.io/alphapy-pro/ +* **Installation**: ``pip install alphapy-pro`` + +Here are just some of the things you can do with **AlphaPy (legacy)**: * Run machine learning models using ``scikit-learn``, ``Keras``, ``xgboost``, ``LightGBM``, and ``CatBoost``. * Generate blended or stacked ensembles. @@ -21,10 +31,29 @@ some of the things you can do with AlphaPy: :alt: AlphaPy Model Pipeline :align: center -AlphaPy Pro: Coming Soon ------------------------- +AlphaPy Pro: Now Available! +--------------------------- + +**AlphaPy Pro** is the next generation of AlphaPy with enhanced features and modern capabilities: + +* **Modern Python 3.12+** support with UV package management +* **Enhanced MarketFlow** with advanced financial ML features +* **MetaLabeling Support** for sophisticated financial modeling +* **NLP Features** for sentiment analysis and text processing +* **Automated CI/CD** with GitHub Actions and PyPI publishing +* **Comprehensive Documentation** with tutorials and examples + +**Quick Start with AlphaPy Pro**:: + + pip install alphapy-pro + +**Links**: + +* **GitHub Repository**: https://github.com/ScottfreeLLC/alphapy-pro +* **Documentation**: https://scottfreellc.github.io/alphapy-pro/ +* **PyPI Package**: https://pypi.org/project/alphapy-pro/ -https://www.scottfreellc.com/alphapy-pro +**Note**: Active development has moved to AlphaPy Pro. This repository (AlphaPy) remains available for users who rely on the original version. Documentation -------------