syntax.us Let the syntax do the talking
Blog Contact Posts Questions Tags Hire Me

Advertising: 10 Weeks Time Series Data Science Class $400
http://www.scae.org/HTDataScience.pdf

Question:
How to use Stock Market to Learn Python?

This is/will be a presentation at a Meetup:

http://www.meetup.com/BAyPIGgies/events/228208164

This presentation has a take-home test:

python_preso_questions
(I encourage you to use google, stackoverflow, and your friends. Also, some of the questions answer some of the questions so watch for that.)

Use Case Discussion

  • Ted the Trader lives in California; he uses his lunch hour to rebalance his position in SPY once a day.
  • On a Tuesday at 12:50 PM he sees a new prediction appear at http://www.pyspy.info and that prediction is bullish.
  • Ted looks at his current position of SPY inside his brokerage account and sees that it is bearish.
  • At 12:55 PM Ted rebalances his position of SPY so that it is bullish.
  • Ted is done trading for that Tuesday.

Development Environment Setup

I use Ubuntu 14 to develop and run pyspy.

I like this release:

http://releases.ubuntu.com/14.04/ubuntu-14.04.3-desktop-amd64.iso

I prefer to run Ubuntu on a newish-laptop. I can usually replace windows-10 with Ubuntu in about 9 minutes. After the initial Ubuntu bootup, I need about 30 minutes to install all the latest Ubuntu updates.

A suitable newish-laptop should cost less than $500. Ubuntu friendly CPUs are i3, i5, i7, or A10; avoid Pentium.

If Ubuntu fails to install on the newish-laptop, I return the laptop to the store for a refund.

Stores which behave well: Costco, Walmart, and BestBuy .

If I want to leave windows-10 on the laptop, I can use a USB drive to power Linux on the laptop while windows-10 sleeps.

Another way to run Ubuntu is inside of a VirtualBox on my Mac.

It is possible to run Ubuntu inside of a VirtualBox inside of windows-10 but that setup often displays bad behavior.

It is possible to build a Mac-only development environment but that environment becomes more cumbersome as the ML-app grows in complexity.

It is possible to build a complex ML-app which runs on both Mac and Ubuntu but my attitude is 'why bother?'

Instead I just pick the OS which will support the production-app, in this case Ubuntu, and then use that OS to host my development environment.

During installation the Ubuntu wizard will ask my name and where I live.

For the pyspy-app the proper name is: 'ann'

Ubuntu will then create a privileged account named: 'ann'

Also I should tell the wizard that I live in California so that predictions generated at 12:50pm will be 10 minutes before market close at 13:00 pm.

After I install Ubuntu, I run shell commands to enhance it:

sudo apt-get update
sudo apt-get upgrade

sudo apt-get install autoconf bison build-essential libssl-dev libyaml-dev \
libreadline6-dev zlib1g-dev libncurses5-dev libffi-dev libgdbm3       \
libgdbm-dev libsqlite3-dev gitk postgresql postgresql-server-dev-all  \
libpq-dev emacs wget curl chromium-browser openssh-server aptitude    \
ruby ruby-dev sqlite3

sudo apt-get update
sudo apt-get upgrade
With that behind me I should install ta-lib:

mkdir -p ~/Downloads
cd       ~/Downloads
rm -rf ta-lib ta-lib-0.4.0-src.tar.gz
wget http://prdownloads.sourceforge.net/ta-lib/ta-lib-0.4.0-src.tar.gz
tar zxf ta-lib-0.4.0-src.tar.gz
cd ta-lib
./configure --prefix=/usr
make
sudo make install
ls -l /usr/lib/libta*
ls -l /usr/include/ta-lib
ls -l /usr/bin/ta-lib-config
cat   /usr/bin/ta-lib-config
After I install ta-lib, I should install python:

cd ~
rm -rf anaconda3
mkdir -p ~/Downloads
cd       ~/Downloads
rm -rf   ~/Downloads/Anaconda3-2.4.1-Linux-x86_64.sh
wget https://3230d63b5fc54e62148e-c95ac804525aac4b6dba79b00b39d1d3.ssl.cf1.rackcdn.com/Anaconda3-2.4.1-Linux-x86_64.sh
bash   Anaconda3-2.4.1-Linux-x86_64.sh
echo 'export PATH=${HOME}/anaconda3/bin:$PATH' >> ~/.bashrc
bash
cd ${HOME}/anaconda3/bin
mv curl curl_anaconda3
cd ~
which python
python -V
python -c 'print(1.1+2.2)'
Then, I should pip install TA-lib:
pip install TA-Lib
python -c 'import numpy, talib'

Data Acquistion (Automated)

For pyspy, automated data acquistion is simple.
  • Create a folder for data
  • Clone a repo with a script to copy data into the folder
  • Create a cron entry to run the script at 12:50 M-F.
  • I list syntax below to do this:

mkdir -p ~/ddata
cd ~
git clone https://github.com/danbikle/pyspy
crontab ~/pyspy/crontab.txt
crontab -l
The crontab in the above example assumes that you are in the California timezone.

If you are in a different timezone, study the comments in ~/pyspy/crontab.txt to see what you should change.

Data Acquistion (Manual at Night)

To manually acquire data, run this shell script:
~/pyspy/bin/night.bash
I captured some screen output when I did that:

dan@nia111:~/pyspy $ 
dan@nia111:~/pyspy $ 
dan@nia111:~/pyspy $ bin/night.bash 
--2016-01-05 02:55:48--  http://ichart.finance.yahoo.com/table.csv?s=%5EGSPC
Resolving ichart.finance.yahoo.com (ichart.finance.yahoo.com)... 208.71.44.31
Connecting to ichart.finance.yahoo.com (ichart.finance.yahoo.com)|208.71.44.31
HTTP request sent, awaiting response... 200 OK
Length: unspecified [text/csv]
Saving to: ‘GSPC.csv’

    [                <=>                    ] 1,195,373    321KB/s   in 3.6s   

2016-01-05 02:55:52 (321 KB/s) - ‘GSPC.csv’ saved [1195373]

I am building features from this file:
GSPC2.csv
Busy...
I am building features from this file:
ftrGSPC2.csv
Busy...
True  positive count: 63
False positive count: 61
True  negative count: 72
False negative count: 56
Accuracy of positive predictions: 50.806%
Accuracy of negative predictions: 56.25%
Combined Accuracy of predictions: 53.571%
Positive Effectiveness: 0.0276%
Negative Effectiveness: -0.034%
Long Only Effectiveness: -0.004%
hello, from /home/dan/pyspy/py/plotem.py
Plotting data from: learn_test.csv
New png file: 
learn_test.png
dan@nia111:~/pyspy $ 
dan@nia111:~/pyspy $ 

Data Acquistion (Manual at Near Calif-Noon)

When the market is near close (California lunch hour) sometimes I will manually acquire data:
~/pyspy/bin/noon50.bash
I captured some screen output when I did that:

dan@nia111:~/pyspy $ 
dan@nia111:~/pyspy $ 
dan@nia111:~/pyspy $ bin/noon50.bash 
--2016-01-05 03:03:02--  http://ichart.finance.yahoo.com/table.csv?s=%5EGSPC
Resolving ichart.finance.yahoo.com (ichart.finance.yahoo.com)... 208.71.44.31
Connecting to ichart.finance.yahoo.com (ichart.finance.yahoo.com)|208.71.44.31
HTTP request sent, awaiting response... 200 OK
Length: unspecified [text/csv]
Saving to: ‘GSPC.csv’

    [         <=>                           ] 1,195,373    555KB/s   in 2.1s   

2016-01-05 03:03:05 (555 KB/s) - ‘GSPC.csv’ saved [1195373]

--2016-01-05 03:03:05--  http://finance.yahoo.com/q?s=%5EGSPC
Resolving finance.yahoo.com (finance.yahoo.com)... 208.71.44.30, 208.71.44.31
Connecting to finance.yahoo.com (finance.yahoo.com)|208.71.44.30
HTTP request sent, awaiting response... 200 OK
Length: unspecified [text/html]
Saving to: ‘GSPC.html’

    [ <=>                            ] 73,293      --.-K/s   in 0.09s   

2016-01-05 03:03:05 (770 KB/s) - ‘GSPC.html’ saved [73293]

I am building features from this file:
GSPC2.csv
Busy...
I am building features from this file:
ftrGSPC2.csv
Busy...
True  positive count: 63
False positive count: 61
True  negative count: 72
False negative count: 56
Accuracy of positive predictions: 50.806%
Accuracy of negative predictions: 56.25%
Combined Accuracy of predictions: 53.571%
Positive Effectiveness: 0.0276%
Negative Effectiveness: -0.034%
Long Only Effectiveness: -0.004%
hello, from /home/dan/pyspy/py/plotem.py
Plotting data from: learn_test.csv
New png file: 
learn_test.png
dan@nia111:~/pyspy $ 
dan@nia111:~/pyspy $ 
dan@nia111:~/pyspy $ 
The script noon50.bash is an enhanced version of night.bash

The script noon50.bash gets the most recent price by scraping a web-page with python.

After you study noon50.bash and night.bash you should notice that they do more than just collect data. They also do the following tasks:
  • Create Features
  • Build a model with scikit-learn
  • Build Visualization of OOS predictions
The above three topics are discussed below.

Feature Creation with Python

When I talk with stock-traders about how they decide to open or close a position, often they will describe an example of a price chart:



For example they might overlay a simple-moving-average on top of a price-time-series and then make decisions based on rules which they believe are true.

An example of a rule might be this statement: When price is below a 5-day-moving average, I should be bullish.

A Python programmer might look at that rule and ask, "How to use Python to build a 5-day-moving average and then compare it to the price?"

The answer is, it is relatively easy to do this calculation in Python.

As a product manager I would describe how I want to interact with this calculation.

Currently, I would be happy if the calculation was placed within a simple 2D Python-List:
[
["2015-12-01",2102.63,price_over5d_sma]
,["2015-12-02",2079.51,price_over5d_sma]
,["2015-12-03",2049.62,price_over5d_sma]
,["2015-12-04",2091.69,price_over5d_sma]
]
As a programmer I would look at the above example and then write this python code:

# cp_o5dmvg_avg.py

# This script should demo how to create a list of observations of cp over 5day mvg avg.

import statistics

# Here is some data:
d_cp0_l = [
['2015-12-31',2043.94]
,['2015-12-30',2063.36]
,['2015-12-29',2078.36]
,['2015-12-28',2056.50]
,['2015-12-24',2060.98]
,['2015-12-23',2064.29]
,['2015-12-22',2038.96]
,['2015-12-21',2021.15]
,['2015-12-18',2005.55]
,['2015-12-17',2041.89]
,['2015-12-16',2073.07]
,['2015-12-15',2043.41]
,['2015-12-14',2021.93]
,['2015-12-11',2012.36]
,['2015-12-10',2052.22]
,['2015-12-09',2047.61]
,['2015-12-08',2063.59]
,['2015-12-07',2077.07]
,['2015-12-04',2091.68]
,['2015-12-03',2049.62]
,['2015-12-02',2079.51]
,['2015-12-01',2102.62]
]

# I should reverse the order of the data so it ascends by date:
d_cp_l = [row for row in reversed(d_cp0_l)]

# I should get the column of prices:
cp_a = [row[1] for row in d_cp_l]

# I should collect a moving avg:
mvgavg5 = []
for rn in range(1,1+len(cp_a)):
  rn0 = rn-5
  if rn0 < 0:
    rn0 = 0
  avgthis = cp_a[rn0:rn]
  mvgavg5.append(statistics.mean(avgthis))

# I should check my work:
len(mvgavg5) == len(cp_a) # should be true
mvgavg5[0]   == cp_a[0]   # should be true
avgthis      == cp_a[-5+len(cp_a):len(cp_a)] # should be true
avgthis      == [2060.98, 2056.5, 2078.36, 2063.36, 2043.94] # should be true
mvgavg5[-1+len(cp_a)] == sum([2060.98, 2056.5, 2078.36, 2063.36, 2043.94])/5
# should be true

cp_o5dmvg_avg = []
# I should add the feature now:
for rn in range(0,len(cp_a)):
  d_cp_l[rn].append(cp_a[rn]/mvgavg5[rn])

# I should check my work:
d_cp_l[0] == ['2015-12-01',2102.62,2102.62/2102.62] # should be true
d_cp_l[1] == ['2015-12-02',2079.51,2079.51/((2079.51+2102.62)/2)] # should be true
d_cp_l[len(cp_a)-1] == ['2015-12-31',2043.94,2043.94/(sum([2060.98, 2056.5, 2078.36, 2063.36, 2043.94])/5)] 
# should be true

print(d_cp_l)


Output:


[['2015-12-01', 2102.62, 1.0], ['2015-12-02', 2079.51, 0.9944741076915352], ['2015-12-03', 2049.62, 0.9866987603803105], ['2015-12-04', 2091.68, 1.0052009808456368], ['2015-12-07', 2077.07, 0.9985433392625356], ['2015-12-08', 2063.59, 0.9957998237701793], ['2015-12-09', 2047.61, 0.9911399990512675], ['2015-12-10', 2052.22, 0.9931214836767105], ['2015-12-11', 2012.36, 0.9813661567271538], ['2015-12-14', 2021.93, 0.991364727963435], ['2015-12-15', 2043.41, 1.003883063965422], ['2015-12-16', 2073.07, 1.0159129823708541], ['2015-12-17', 2041.89, 1.0016472638153338], ['2015-12-18', 2005.55, 0.9844784676782006], ['2015-12-21', 2021.15, 0.9922121301080897], ['2015-12-22', 2038.96, 1.0013928424791416], ['2015-12-23', 2064.29, 1.0147082533740208], ['2015-12-24', 2060.98, 1.0111834739322123], ['2015-12-28', 2056.5, 1.003966068729569], ['2015-12-29', 2078.36, 1.0090017661754582], ['2015-12-30', 2063.36, 0.9993519633379798], ['2015-12-31', 2043.94, 0.9919014979899333]]


Feature Creation with NumPy

As a Product Manager I would want more features than just current_price / mvg_avg.

The code below demonstrates how to create more features from prices:

# genf_demo.py

# This script should demo how to generate features from prices.

# The syntax here comes mostly from this file:
# https://github.com/danbikle/pyspy/blob/master/py/genf.py

import numpy  as np

# Here is some data:
d_cp0_l = [
['2015-12-31',2043.94]
,['2015-12-30',2063.36]
,['2015-12-29',2078.36]
,['2015-12-28',2056.50]
,['2015-12-24',2060.98]
,['2015-12-23',2064.29]
,['2015-12-22',2038.96]
,['2015-12-21',2021.15]
,['2015-12-18',2005.55]
,['2015-12-17',2041.89]
,['2015-12-16',2073.07]
,['2015-12-15',2043.41]
,['2015-12-14',2021.93]
,['2015-12-11',2012.36]
,['2015-12-10',2052.22]
,['2015-12-09',2047.61]
,['2015-12-08',2063.59]
,['2015-12-07',2077.07]
,['2015-12-04',2091.68]
,['2015-12-03',2049.62]
,['2015-12-02',2079.51]
,['2015-12-01',2102.62]
]

# I should reverse the order of the data so it ascends by date:
d_cp_l = [row for row in reversed(d_cp0_l)]

# I should get the column of prices:
cp = [row[1] for row in d_cp_l]

cplag1_l = [cp[0]] + cp
cplag2_l = [cp[0],cp[0]]             + cp
cplag4_l = [cp[0],cp[0],cp[0],cp[0]] + cp
cplag8_l = [cp[0],cp[0],cp[0],cp[0]] + cplag4_l
# I should snip off ends so new columns are as long as cp:
cplag1_l = cplag1_l[:-1]
cplag2_l = cplag2_l[:-2]
cplag4_l = cplag4_l[:-4]
cplag8_l = cplag8_l[:-8]

# NumPy allows me to do arithmetic on its Arrays.
# I should convert my lists to Arrays:
cp_a     = np.array(cp)
cplag1_a = np.array(cplag1_l)
cplag2_a = np.array(cplag2_l)
cplag4_a = np.array(cplag4_l)
cplag8_a = np.array(cplag8_l)

# I should calculate pct-deltas:
pctlag1_a = 100.0 * (cp_a - cplag1_a)/cplag1_a
pctlag2_a = 100.0 * (cp_a - cplag2_a)/cplag2_a
pctlag4_a = 100.0 * (cp_a - cplag4_a)/cplag4_a
pctlag8_a = 100.0 * (cp_a - cplag8_a)/cplag8_a

# Now I should have 4 features:
# pctlag1_a
# pctlag2_a
# pctlag4_a
# pctlag8_a


Feature Creation with TA-lib

The code below demonstrates how to create more features from Bollinger Bands calculated using TA-lib:

# genf_demo_talib.py

import numpy  as np
import talib
import pdb

# Here is some data:
d_cp0_l = [
['2015-12-31',2043.94]
,['2015-12-30',2063.36]
,['2015-12-29',2078.36]
,['2015-12-28',2056.50]
,['2015-12-24',2060.98]
,['2015-12-23',2064.29]
,['2015-12-22',2038.96]
,['2015-12-21',2021.15]
,['2015-12-18',2005.55]
,['2015-12-17',2041.89]
,['2015-12-16',2073.07]
,['2015-12-15',2043.41]
,['2015-12-14',2021.93]
,['2015-12-11',2012.36]
,['2015-12-10',2052.22]
,['2015-12-09',2047.61]
,['2015-12-08',2063.59]
,['2015-12-07',2077.07]
,['2015-12-04',2091.68]
,['2015-12-03',2049.62]
,['2015-12-02',2079.51]
,['2015-12-01',2102.62]
]

# I should reverse the order of the data so it ascends by date:
d_cp_l = [row for row in reversed(d_cp0_l)]

# I should get the column of prices:
cp   = [row[1] for row in d_cp_l]
cp_a = np.array(cp)

# I should calculate Bollinger Bands:
upper_a, middle_a, lower_a = talib.BBANDS(cp_a,timeperiod=5, nbdevup=2, nbdevdn=2, matype=0)

# Now that I have 3 Bollinger Bands,
# I combine them with current price to get 3 features:
upper_o_cp  = upper_a  / cp_a
middle_o_cp = middle_a / cp_a
cp_o_lower  = cp_a / lower_a

# I should now have 3 more features:
# upper_o_cp
# middle_o_cp
# cp_o_lower


Feature Visualization with Pandas

Pandas offers a syntax for applying predicates to tabular data.

I can use this syntax to visualize dependency between features and future deltas:
# pandas_demo.py

import pandas as pd
import numpy  as np

# Here is some data:
d_cp_l = [
['2015-12-31',2043.94]
,['2015-12-30',2063.36]
,['2015-12-29',2078.36]
,['2015-12-28',2056.50]
,['2015-12-24',2060.98]
,['2015-12-23',2064.29]
,['2015-12-22',2038.96]
,['2015-12-21',2021.15]
,['2015-12-18',2005.55]
,['2015-12-17',2041.89]
,['2015-12-16',2073.07]
,['2015-12-15',2043.41]
,['2015-12-14',2021.93]
,['2015-12-11',2012.36]
,['2015-12-10',2052.22]
,['2015-12-09',2047.61]
,['2015-12-08',2063.59]
,['2015-12-07',2077.07]
,['2015-12-04',2091.68]
,['2015-12-03',2049.62]
,['2015-12-02',2079.51]
,['2015-12-01',2102.62]
]

# I should reverse the order of the data so it ascends by date:
d_cp_l = [row for row in reversed(d_cp_l)]

# I should get the column of date strings
cdate_l = [row[0] for row in d_cp_l]

# I should get the column of prices:
cp   = [row[1] for row in d_cp_l]

# Work towards dependent array:
cplead_l = cp + [cp[-1]]

# Work towards independent array:
cplag1_l = [cp[0]] + cp

# I should snip off ends so new columns as long as cp:
cplead_l = cplead_l[1:]
cplag1_l = cplag1_l[:-1]

# NumPy allows me to do arithmetic on its Arrays.
# I should convert my lists to Arrays:
cp_a     = np.array(cp)
cplead_a = np.array(cplead_l)
cplag1_a = np.array(cplag1_l)

# I should use simple arithmetic to calculate pct-deltas:
pctlead_a = 100.0 * (cplead_a - cp_a)/cp_a
pctlag1_a = 100.0 * (cp_a - cplag1_a)/cplag1_a

# I should put my columns into a DataFrame:
df1            = pd.DataFrame(cdate_l)
df1.columns    = ['cdate']
df1['cp']      = cp
df1['pctlag1'] = pctlag1_a
df1['pctlead'] = pctlead_a

# I should create simple predicates:
pred_lt0 = df1['pctlag1'] < 0
pred_gt0 = df1['pctlag1'] > 0

# I should apply them:
lt0_df = df1[pred_lt0]
gt0_df = df1[pred_gt0]

# I should study pctlead:
pctlead_lt0_df     = lt0_df['pctlead']
pctlead_lt0_mean_n = np.mean(pctlead_lt0_df)
print(pctlead_lt0_mean_n)

pctlead_gt0_df     = gt0_df['pctlead']
pctlead_gt0_mean_n = np.mean(pctlead_gt0_df)
print(pctlead_gt0_mean_n)

# I can stack predicates which then acts as AND:
pred_lt0  = df1['pctlag1'] < 0
pred_gtm1 = df1['pctlag1'] > -1
lt0_gtm1_df = df1[pred_lt0][pred_gtm1]
print(lt0_gtm1_df)

Model Building with scikit-learn

On github, a link to the Python script I use to build Logistic Regression model from my features is listed below:

https://github.com/danbikle/pyspy/blob/master/py/learn_test.py

The syntax which creates a model is 3 simple lines of Python:
from sklearn import linear_model
lrmodel = linear_model.LogisticRegression()
lrmodel.fit(x_train_a, label_train_a)
Much of my effort behind this script supported creation of the the two Numpy Arrays which I feed to lrmodel.fit():
  • x_train_a
  • label_train_a
The first array, x_train_a, is a simple array of features which I discussed earlier in this page.

The second array, label_train_a, is an array of what I call 'labels'. A label is one of two values: True or False

Both arrays are created from all observations between 1987 and 2015 which is about (2015-1987)*252 observations.

My intent here is to create a model which will help me predict if a future observation is above the training median, True or False?

In Python terms, a future observation is a simple array like this:
[pctlag1, pctlag2, pctlag4, pctlag8, upf, lowf]
An actual future observation might look like this:
[-0.127,0.791,0.000,-1.194,-2.056, 1.002, 1.004]

Prediction of Out-of-Sample (OOS) data

After I create the model, I have a Python object called lrmodel.

I can use this model to make predictions with appropriate calls to the sklearn API.

For example I could make a single prediction with syntax like this:
xoos_a      = [-0.127,0.791,0.000,-1.194,-2.056, 1.002, 1.004]
xf_a        = xoos_a.astype(float)
xr_a        = xf_a.reshape(1, -1)
aprediction = lrmodel.predict_proba(xr_a)[0,1]
Then I will see my prediction in the variable, aprediction.

The prediction should be a value between 0 and 1.

A value of 0.55 is a signal that the model is 55% confident that the label for the observation:
[-0.127,0.791,0.000,-1.194,-2.056, 1.002, 1.004]
will be True. I am interested in more than one prediction; I want all the predictions for 2015.

To get those I use a loop:

predictions_l = []
for xoos_a in x_test_a:
  xf_a        = xoos_a.astype(float)
  xr_a        = xf_a.reshape(1, -1)
  aprediction = lrmodel.predict_proba(xr_a)[0,1]
  predictions_l.append(aprediction)

Visualization of OOS predictions

After I collected the 2015 predictions, I had some questions:
  • How accurate where the predictions?
  • How 'effective' where they?
  • How to best visualize their effectiveness?
Measuring accuracy is easy.

For example if I have 10 predictions and 6 of them are correct (AKA 'True') then I have 60% accuracy.

Usually I like to categorize my accuracy calculations into Postive accuracy and Negative accuracy. If I have a Negative accuracy of 60% and a Postive accuracy of 50% then I see that my model is better at predicting when the market will go down.

That would be useful information if I am gambling money with these predictions.

When I study the above Python syntax I see that the 2015 predictions accumulate in a list named predictions_l via this line:
predictions_l.append(aprediction)
I rely on Pandas to help me calculate prediction accuracy.

I start this effort by transforming predictions_l into +1 or -1 using syntax like this:
test_df['pdir'] = [np.sign(prediction-0.5) for prediction in predictions_l]
The above line captures prediction 'direction' with a column named 'pdir' in a Pandas DataFrame.

Next, I capture the actual price direction using syntax like this:
cp_l   = list(test_df['cp'].values)
lead_l = cp_l[1:] + [cp_l[len(cp_l)-1]]
lead_a = np.array(lead_l) - np.array(cp_l)
test_df['actual_dir'] = [np.sign(lead_delta) for lead_delta in lead_a]
Earlier in this page I showed some Pandas predicate syntax to visualize dependency between features and future deltas.

Now I show more Pandas predicate syntax to calculate accuracies:
# I should count positive predictions.
posp_df  = test_df[['pdir','actual_dir']][test_df['pdir'] == 1]
# I should count true positive predictions.
tposp_df = posp_df[posp_df['actual_dir'] == 1]
# I should calculate positive accuracy.
pos_acc  = 100.0 * len(tposp_df)/len(posp_df)
# I should count negative predictions.
negp_df  = test_df[['pdir','actual_dir']][test_df['pdir'] == -1]
# I should count true negative predictions.
tnegp_df = negp_df[negp_df['actual_dir'] == -1]
# I should calculate negative accuracy.
neg_acc  = 100.0 * len(tnegp_df)/len(negp_df)
# I should calculate combined accuracy
com_acc  = 100.0 * (len(tposp_df)+len(tnegp_df))/len(test_df)
After I see the accuracy, I want to know the effectiveness of the predictions.

The idea behind effectiveness can be described with a simple sentence:

Get all the positive predictions, then calculate their average gain.

Also I should get all the negative predictions and then calculate their average gain.

If the average gain of the positive predictions is higher than the gain of the negative predictions I interpret that to mean that the model is effective.

The syntax to calculate effectiveness is displayed below:
pos_pctlead_a = np.array(test_df[['pctlead']][test_df['pdir'] == 1])
pos_eff       = np.mean(pos_pctlead_a)
neg_pctlead_a = np.array(test_df[['pctlead']][test_df['pdir'] == -1])
neg_eff       = np.mean(neg_pctlead_a)
After doing the above calculations of accuracy and effectiveness, I should show their values:
# I should report
print('True  positive count: '+str(len(tposp_df)))
print('False positive count: '+str(len(posp_df)-len(tposp_df)))
print('True  negative count: '+str(len(tnegp_df)))
print('False negative count: '+str(len(negp_df)-len(tnegp_df)))
print('Accuracy of positive predictions: ' +str(pos_acc)[:6]+'%')
print('Accuracy of negative predictions: ' +str(neg_acc)[:6]+'%')
print('Combined Accuracy of predictions: ' +str(com_acc)[:6]+'%')
print('Positive Effectiveness: '  +str(pos_eff)[:6]+'%')
print('Negative Effectiveness: '  +str(neg_eff)[:6]+'%')
print('Long Only Effectiveness: ' +str(longonly_eff)[:6]+'%')
Sample output:
True  positive count: 63
False positive count: 61
True  negative count: 72
False negative count: 56
Accuracy of positive predictions: 50.806%
Accuracy of negative predictions: 56.25%
Combined Accuracy of predictions: 53.571%
Positive Effectiveness: 0.0276%
Negative Effectiveness: -0.034%
Long Only Effectiveness: -0.004%
In addition to the above report, I like to create a plot which I call the "Blue-Line Green-Line Visualization".

The phrases below describe the main ideas behind the plot:
  • Blue-Line shows price.
  • Green-Line shows effectiveness.
  • If price goes up, Blue-Line goes up.
  • If prediction is accurate or effective, Green-Line goes up.
  • If Blue-Line shows a delta of 1%, Green-Line should show delta of 1%.
  • If prediction is Bullish, Blue-Line and Green-Line move in same direction.
  • If prediction is Bearish, Blue-Line and Green-Line move in opposite directions.
After I see the above list of behaviors, I am ready to code some Python to collect data for the plot.

I start by seeing that I have my predictions accumulated in a list called: predictions_l

I copy values from predictions_l into a data frame column:
test_df['prediction'] = predictions_l
Earlier in the script, test_df had been created from a CSV file full of test data using syntax like this:
testf   = sys.argv[2]
test_df = pd.read_csv(testf)
After I create test_df['prediction'] from predictions_l, I calculate what I call prediction direction and also copy those calculations into test_df:
# I should capture prediction 'direction'. Above 0.5 is bullish else bearish.
test_df['pdir'] = [np.sign(prediction-0.5) for prediction in predictions_l]
Next, I calculate 'lead_delta' which is the amount Blue-Line change each day:
# I should capture lead-price-deltas to help with later visualization.
cp_l = list(test_df['cp'].values)
lead_l = cp_l[1:] + [cp_l[len(cp_l)-1]]
lead_a = np.array(lead_l) - np.array(cp_l)
test_df['lead_delta'] = list(lead_a)
Then, I calculate 'actual_dir' which is either +1 or -1 like 'pdir', the predicted direction:
test_df['actual_dir'] = [np.sign(lead_delta) for lead_delta in lead_a]
Next, I work towards collecting data to plot the green line:

# I should declare some integers to help me navigate the Array.
cp_i         =  1
prediction_i =  9
pdir_i       = 10
lead_delta_i = 11
# I should get the array,
# but the result of last prediction unknown,
# so avoid it:
bg_a = (test_df.values)[:-1]
# I should calculate values of greenline
greenline_l = []
# Blue-line and greenline should start at same place:
greenline_l.append(bg_a[0,cp_i])
# I should look at each row in bg_a
row_i = 0
for row in bg_a:
  greenline_l.append(greenline_l[row_i]+(row[pdir_i] * row[lead_delta_i]))
  row_i += 1
# I should add greenline_l to test_df so I can chart it later
test_df['greenline'] = greenline_l
# I should save test_df as CSV file:
test_df.to_csv('learn_test.csv', float_format='%4.3f', index=False)
After the above script runs, I should have an artifact named learn_test.csv and it should look like the text listed below:

cdate,cp,pctlead,pctlag1,pctlag2,pctlag4,pctlag8,upf,lowf,prediction,pdir,lead_delta,actual_dir,greenline
2015-01-02,2058.200,-1.828,-0.034,-1.065,-1.464,-0.601,1.022,1.005,0.509,1.000,-37.620,-1.000,2058.200
2015-01-05,2020.580,-0.889,-1.828,-1.861,-3.348,-2.788,1.044,1.003,0.539,1.000,-17.970,-1.000,2020.580
2015-01-06,2002.610,1.163,-0.889,-2.701,-3.737,-3.821,1.049,1.008,0.544,1.000,23.290,1.000,2002.610
2015-01-07,2025.900,1.789,1.163,0.263,-1.603,-2.689,1.025,1.018,0.506,1.000,36.240,1.000,2025.900
2015-01-08,2062.140,-0.840,1.789,2.973,0.191,-1.275,1.008,1.037,0.477,-1.000,-17.330,-1.000,2062.140
2015-01-09,2044.810,-0.809,-0.840,0.933,1.199,-2.189,1.013,1.027,0.526,1.000,-16.550,-1.000,2079.470
2015-01-12,2028.260,-0.258,-0.809,-1.643,1.281,-2.504,1.022,1.018,0.548,1.000,-5.230,-1.000,2062.920
2015-01-13,2023.030,-0.581,-0.258,-1.065,-0.142,-1.742,1.021,1.008,0.527,1.000,-11.760,-1.000,2057.690
2015-01-14,2011.270,-0.925,-0.581,-0.838,-2.467,-2.280,1.029,1.006,0.521,1.000,-18.600,-1.000,2045.930
2015-01-15,1992.670,1.342,-0.925,-1.501,-2.550,-1.381,1.031,1.004,0.521,1.000,26.750,1.000,2027.330
2015-01-16,2019.420,0.155,1.342,0.405,-0.436,0.839,1.010,1.015,0.479,-1.000,3.130,1.000,2054.080
2015-01-20,2022.550,0.473,0.155,1.499,-0.024,-0.165,1.007,1.016,0.492,-1.000,9.570,1.000,2050.950
2015-01-21,2032.120,1.527,0.473,0.629,1.037,-1.456,1.005,1.022,0.511,1.000,31.030,1.000,2041.380
2015-01-22,2063.150,-0.549,1.527,2.007,3.537,0.897,1.004,1.042,0.484,-1.000,-11.330,-1.000,2072.410
2015-01-23,2051.820,0.257,-0.549,0.969,1.604,1.162,1.010,1.024,0.498,-1.000,5.270,1.000,2083.740
2015-01-26,2057.090,-1.339,0.257,-0.294,1.708,1.684,1.009,1.021,0.496,-1.000,-27.540,-1.000,2078.470
2015-01-27,2029.550,-1.350,-1.339,-1.085,-0.126,0.909,1.022,1.005,0.514,1.000,-27.390,-1.000,2106.010
2015-01-28,2002.160,0.953,-1.350,-2.670,-2.956,0.476,1.042,1.003,0.516,1.000,19.090,1.000,2078.620
2015-01-29,2021.250,-1.299,0.953,-0.409,-1.490,0.091,1.025,1.015,0.490,-1.000,-26.260,-1.000,2097.710
2015-01-30,1994.990,1.296,-1.299,-0.358,-3.019,-1.363,1.035,1.009,0.513,1.000,25.860,1.000,2123.970
2015-02-02,2020.850,1.444,1.296,-0.020,-0.429,-0.555,1.009,1.017,0.495,-1.000,29.180,1.000,2149.830
2015-02-03,2050.030,-0.416,1.444,2.759,2.391,-0.636,1.003,1.036,0.486,-1.000,-8.520,-1.000,2120.650
2015-02-04,2041.510,1.029,-0.416,1.022,1.002,-0.502,1.011,1.027,0.507,1.000,21.010,1.000,2129.170
2015-02-05,2062.520,-0.342,1.029,0.609,3.385,0.264,1.009,1.038,0.503,1.000,-7.050,-1.000,2150.180
2015-02-06,2055.470,-0.425,-0.342,0.684,1.713,1.277,1.009,1.019,0.498,-1.000,-8.730,-1.000,2143.130
2015-02-09,2046.740,1.068,-0.425,-0.765,-0.160,2.227,1.009,1.005,0.493,-1.000,21.850,1.000,2151.860
2015-02-10,2068.590,-0.003,1.068,0.638,1.326,2.342,1.003,1.016,0.476,-1.000,-0.060,-1.000,2130.010
2015-02-11,2068.530,0.964,-0.003,1.065,0.291,3.686,1.004,1.012,0.466,-1.000,19.950,1.000,2130.070
2015-02-12,2088.480,0.407,0.964,0.962,1.606,3.347,1.003,1.025,0.467,-1.000,8.510,1.000,2110.120
2015-02-13,2096.990,0.160,0.407,1.376,2.455,2.291,1.006,1.029,0.481,-1.000,3.350,1.000,2101.610
2015-02-17,2100.340,-0.031,0.160,0.568,1.535,2.882,1.005,1.021,0.480,-1.000,-0.660,-1.000,2098.260
2015-02-18,2099.680,-0.106,-0.031,0.128,1.506,1.802,1.007,1.016,0.494,-1.000,-2.230,-1.000,2098.920
2015-02-19,2097.450,0.613,-0.106,-0.138,0.429,2.042,1.004,1.004,0.490,-1.000,12.850,1.000,2101.150
2015-02-20,2110.300,-0.030,0.613,0.506,0.635,3.105,1.000,1.009,0.471,-1.000,-0.640,-1.000,2088.300
2015-02-23,2109.660,0.276,-0.030,0.582,0.444,1.985,1.002,1.008,0.484,-1.000,5.820,1.000,2088.940
2015-02-24,2115.480,-0.077,0.276,0.245,0.752,2.270,1.002,1.011,0.483,-1.000,-1.620,-1.000,2083.120
2015-02-25,2113.860,-0.148,-0.077,0.199,0.782,1.215,1.004,1.008,0.496,-1.000,-3.120,-1.000,2084.740
2015-02-26,2110.740,-0.296,-0.148,-0.224,0.021,0.656,1.003,1.002,0.501,1.000,-6.240,-1.000,2087.860
2015-02-27,2104.500,0.612,-0.296,-0.443,-0.245,0.198,1.007,1.001,0.506,1.000,12.890,1.000,2081.620
2015-03-02,2117.390,-0.454,0.612,0.315,0.090,0.843,1.002,1.007,0.489,-1.000,-9.610,-1.000,2094.510
2015-03-03,2107.780,-0.439,-0.454,0.156,-0.288,0.493,1.006,1.003,0.500,1.000,-9.250,-1.000,2104.120
2015-03-04,2098.530,0.120,-0.439,-0.891,-0.578,-0.558,1.010,1.002,0.515,1.000,2.510,1.000,2094.870
2015-03-05,2101.040,-1.417,0.120,-0.320,-0.164,-0.409,1.009,1.004,0.507,1.000,-29.780,-1.000,2097.380
2015-03-06,2071.260,0.394,-1.417,-1.299,-2.179,-2.090,1.028,1.001,0.531,1.000,8.170,1.000,2067.600
2015-03-09,2079.430,-1.696,0.394,-1.029,-1.345,-1.629,1.019,1.008,0.514,1.000,-35.270,-1.000,2075.770
2015-03-10,2044.160,-0.192,-1.696,-1.308,-2.591,-3.154,1.037,1.003,0.541,1.000,-3.920,-1.000,2040.500
2015-03-11,2040.240,1.260,-0.192,-1.885,-2.894,-3.053,1.035,1.009,0.530,1.000,25.710,1.000,2036.580
2015-03-12,2065.950,-0.607,1.260,1.066,-0.256,-2.429,1.012,1.018,0.503,1.000,-12.550,-1.000,2062.290
2015-03-13,2053.400,1.353,-0.607,0.645,-1.252,-2.580,1.016,1.013,0.519,1.000,27.790,1.000,2049.740
2015-03-16,2081.190,-0.332,1.353,0.738,1.811,-0.826,1.003,1.027,0.501,1.000,-6.910,-1.000,2077.530
2015-03-17,2074.280,1.216,-0.332,1.017,1.668,-1.274,1.009,1.020,0.516,1.000,25.220,1.000,2070.620
2015-03-18,2099.500,-0.487,1.216,0.880,1.624,1.363,1.003,1.027,0.482,-1.000,-10.230,-1.000,2095.840
2015-03-19,2089.270,0.901,-0.487,0.723,1.747,0.473,1.010,1.020,0.505,1.000,18.830,1.000,2106.070
2015-03-20,2108.100,-0.175,0.901,0.410,1.293,3.128,1.003,1.020,0.472,-1.000,-3.680,-1.000,2124.900
2015-03-23,2104.420,-0.614,-0.175,0.725,1.453,3.146,1.007,1.016,0.479,-1.000,-12.920,-1.000,2128.580
2015-03-24,2091.500,-1.456,-0.614,-0.787,-0.381,1.237,1.010,1.004,0.502,1.000,-30.450,-1.000,2141.500
2015-03-25,2061.050,-0.238,-1.456,-2.061,-1.351,0.373,1.031,1.002,0.521,1.000,-4.900,-1.000,2111.050
2015-03-26,2056.150,0.237,-0.238,-1.690,-2.464,-1.203,1.035,1.008,0.515,1.000,4.870,1.000,2106.150
2015-03-27,2061.020,1.224,0.237,-0.001,-2.062,-0.639,1.026,1.012,0.496,-1.000,25.220,1.000,2111.020
2015-03-30,2086.240,-0.880,1.224,1.463,-0.251,-0.632,1.007,1.022,0.486,-1.000,-18.350,-1.000,2085.800
2015-03-31,2067.890,-0.397,-0.880,0.333,0.332,-1.023,1.010,1.011,0.518,1.000,-8.200,-1.000,2104.150
2015-04-01,2059.690,0.353,-0.397,-1.273,0.172,-2.296,1.014,1.007,0.535,1.000,7.270,1.000,2095.950
2015-04-02,2066.960,0.661,0.353,-0.045,0.288,-1.780,1.010,1.009,0.516,1.000,13.660,1.000,2103.220
2015-04-06,2080.620,-0.206,0.661,1.016,-0.269,-0.520,1.005,1.014,0.493,-1.000,-4.290,-1.000,2116.880
2015-04-07,2076.330,0.268,-0.206,0.453,0.408,0.741,1.004,1.010,0.497,-1.000,5.570,1.000,2121.170
2015-04-08,2081.900,0.446,0.268,0.062,1.078,1.252,1.004,1.013,0.495,-1.000,9.280,1.000,2115.600
2015-04-09,2091.180,0.520,0.446,0.715,1.172,1.463,1.002,1.013,0.487,-1.000,10.880,1.000,2106.320
2015-04-10,2102.060,-0.458,0.520,0.968,1.030,0.758,1.001,1.016,0.490,-1.000,-9.630,-1.000,2095.440
2015-04-13,2092.430,0.163,-0.458,0.060,0.775,1.187,1.007,1.010,0.500,-1.000,3.410,1.000,2105.070
2015-04-14,2095.840,0.515,0.163,-0.296,0.670,1.755,1.005,1.008,0.492,-1.000,10.790,1.000,2101.660
2015-04-15,2106.630,-0.078,0.515,0.679,0.739,1.919,1.001,1.010,0.481,-1.000,-1.640,-1.000,2090.870
2015-04-16,2104.990,-1.131,-0.078,0.437,0.139,1.171,1.003,1.007,0.491,-1.000,-23.810,-1.000,2092.510
2015-04-17,2081.180,0.924,-1.131,-1.208,-0.538,0.234,1.016,1.002,0.517,1.000,19.220,1.000,2116.320
2015-04-20,2100.400,-0.148,0.924,-0.218,0.218,0.889,1.007,1.010,0.490,-1.000,-3.110,-1.000,2135.540
2015-04-21,2097.290,0.509,-0.148,0.774,-0.443,0.292,1.009,1.008,0.494,-1.000,10.670,1.000,2138.650
2015-04-22,2107.960,0.236,0.509,0.360,0.141,0.281,1.004,1.014,0.494,-1.000,4.970,1.000,2127.980
2015-04-23,2112.930,0.225,0.236,0.746,1.526,0.980,1.004,1.017,0.494,-1.000,4.760,1.000,2123.010
2015-04-24,2117.690,-0.414,0.225,0.462,0.823,1.043,1.002,1.012,0.493,-1.000,-8.770,-1.000,2118.250
2015-04-27,2108.920,0.277,-0.414,-0.190,0.555,0.109,1.006,1.006,0.509,1.000,5.840,1.000,2127.020
2015-04-28,2114.760,-0.374,0.277,-0.138,0.323,0.464,1.002,1.005,0.499,-1.000,-7.910,-1.000,2132.860
2015-04-29,2106.850,-1.013,-0.374,-0.098,-0.288,1.233,1.006,1.001,0.495,-1.000,-21.340,-1.000,2140.770
2015-04-30,2085.510,1.092,-1.013,-1.383,-1.520,-0.709,1.021,1.001,0.520,1.000,22.780,1.000,2162.110
2015-05-01,2108.290,0.294,1.092,0.068,-0.030,0.524,1.008,1.011,0.489,-1.000,6.200,1.000,2184.890
2015-05-04,2114.490,-1.184,0.294,1.390,-0.013,0.310,1.006,1.014,0.487,-1.000,-25.030,-1.000,2178.690
2015-05-05,2089.460,-0.446,-1.184,-0.893,-0.825,-1.111,1.016,1.005,0.525,1.000,-9.310,-1.000,2203.720
2015-05-06,2080.150,0.377,-0.446,-1.624,-0.257,-1.773,1.020,1.005,0.532,1.000,7.850,1.000,2194.410
2015-05-07,2088.000,1.346,0.377,-0.070,-0.962,-0.992,1.016,1.009,0.504,1.000,28.100,1.000,2202.260
2015-05-08,2116.100,-0.509,1.346,1.728,0.076,0.063,1.005,1.023,0.478,-1.000,-10.770,-1.000,2230.360
2015-05-11,2105.330,-0.295,-0.509,0.830,0.760,-0.072,1.008,1.017,0.505,1.000,-6.210,-1.000,2241.130
2015-05-12,2099.120,-0.030,-0.295,-0.802,0.912,0.653,1.011,1.013,0.510,1.000,-0.640,-1.000,2234.920
2015-05-13,2098.480,1.078,-0.030,-0.325,0.502,-0.465,1.010,1.007,0.512,1.000,22.620,1.000,2234.280
2015-05-14,2121.100,0.077,1.078,1.047,0.236,0.313,1.002,1.015,0.485,-1.000,1.630,1.000,2256.900
2015-05-15,2122.730,0.305,0.077,1.156,0.826,1.592,1.004,1.017,0.484,-1.000,6.470,1.000,2255.270
2015-05-18,2129.200,-0.064,0.305,0.382,1.433,2.358,1.005,1.019,0.484,-1.000,-1.370,-1.000,2248.800
2015-05-19,2127.830,-0.093,-0.064,0.240,1.399,1.908,1.007,1.014,0.492,-1.000,-1.980,-1.000,2250.170
2015-05-20,2125.850,0.234,-0.093,-0.157,0.224,0.461,1.003,1.003,0.502,1.000,4.970,1.000,2252.150
2015-05-21,2130.820,-0.223,0.234,0.141,0.381,1.211,1.001,1.004,0.492,-1.000,-4.760,-1.000,2257.120
2015-05-22,2126.060,-1.028,-0.223,0.010,-0.147,1.283,1.003,1.001,0.493,-1.000,-21.860,-1.000,2261.880
2015-05-26,2104.200,0.916,-1.028,-1.249,-1.111,0.273,1.018,1.000,0.513,1.000,19.280,1.000,2283.740
2015-05-27,2123.480,-0.127,0.916,-0.121,-0.111,0.112,1.008,1.009,0.495,-1.000,-2.690,-1.000,2303.020
2015-05-28,2120.790,-0.632,-0.127,0.788,-0.471,-0.091,1.009,1.008,0.497,-1.000,-13.400,-1.000,2305.710
2015-05-29,2107.390,0.206,-0.632,-0.758,-0.878,-1.024,1.013,1.004,0.518,1.000,4.340,1.000,2319.110
2015-06-01,2111.730,-0.101,0.206,-0.427,0.358,-0.757,1.008,1.006,0.512,1.000,-2.130,-1.000,2323.450
2015-06-02,2109.600,0.212,-0.101,0.105,-0.654,-0.764,1.008,1.004,0.506,1.000,4.470,1.000,2321.320
2015-06-03,2114.070,-0.862,0.212,0.111,-0.317,-0.786,1.004,1.005,0.505,1.000,-18.230,-1.000,2325.790
2015-06-04,2095.840,-0.144,-0.862,-0.652,-0.548,-1.421,1.012,1.000,0.524,1.000,-3.010,-1.000,2307.560
2015-06-05,2092.830,-0.647,-0.144,-1.005,-0.895,-0.540,1.014,1.003,0.512,1.000,-13.550,-1.000,2304.550
2015-06-08,2079.280,0.042,-0.647,-0.790,-1.437,-2.081,1.021,1.003,0.525,1.000,0.870,1.000,2291.000
2015-06-09,2080.150,1.204,0.042,-0.606,-1.604,-1.916,1.018,1.006,0.515,1.000,25.050,1.000,2291.870
2015-06-10,2105.200,0.174,1.204,1.247,0.447,-0.104,1.002,1.017,0.486,-1.000,3.660,1.000,2316.920
2015-06-11,2108.860,-0.699,0.174,1.380,0.766,-0.136,1.004,1.019,0.496,-1.000,-14.750,-1.000,2313.260
2015-06-12,2094.110,-0.462,-0.699,-0.527,0.713,-0.734,1.011,1.012,0.522,1.000,-9.680,-1.000,2328.010
2015-06-15,2084.430,0.569,-0.462,-1.158,0.206,-1.402,1.016,1.006,0.528,1.000,11.860,1.000,2318.330
2015-06-16,2096.290,0.198,0.569,0.104,-0.423,0.021,1.009,1.008,0.495,-1.000,4.150,1.000,2330.190
2015-06-17,2100.440,0.990,0.198,0.768,-0.399,0.364,1.006,1.009,0.491,-1.000,20.800,1.000,2326.040
2015-06-18,2121.240,-0.530,0.990,1.190,1.296,2.018,1.001,1.022,0.475,-1.000,-11.250,-1.000,2305.240
2015-06-19,2109.990,0.609,-0.530,0.455,1.226,1.435,1.008,1.016,0.497,-1.000,12.860,1.000,2316.490
2015-06-22,2122.850,0.064,0.609,0.076,1.267,0.838,1.004,1.016,0.496,-1.000,1.350,1.000,2303.630
2015-06-23,2124.200,-0.735,0.064,0.673,1.131,0.727,1.005,1.013,0.496,-1.000,-15.620,-1.000,2302.280
2015-06-24,2108.580,-0.297,-0.735,-0.672,-0.597,0.691,1.011,1.002,0.505,1.000,-6.270,-1.000,2317.900
2015-06-25,2102.310,-0.039,-0.297,-1.031,-0.364,0.858,1.013,1.003,0.504,1.000,-0.820,-1.000,2311.630
2015-06-26,2101.490,-2.087,-0.039,-0.336,-1.006,0.248,1.014,1.004,0.499,-1.000,-43.850,-1.000,2310.810
2015-06-29,2057.640,0.266,-2.087,-2.125,-3.133,-2.038,1.042,1.002,0.538,1.000,5.470,1.000,2354.660
2015-06-30,2063.110,0.694,0.266,-1.826,-2.156,-2.740,1.032,1.010,0.527,1.000,14.310,1.000,2360.130
2015-07-01,2077.420,-0.031,0.694,0.961,-1.184,-1.544,1.019,1.017,0.497,-1.000,-0.640,-1.000,2374.440
2015-07-02,2076.780,-0.386,-0.031,0.663,-1.176,-2.170,1.014,1.016,0.511,1.000,-8.020,-1.000,2375.080
2015-07-06,2068.760,0.608,-0.386,-0.417,0.540,-2.610,1.007,1.007,0.533,1.000,12.580,1.000,2367.060
2015-07-07,2081.340,-1.665,0.608,0.220,0.884,-1.292,1.003,1.010,0.511,1.000,-34.660,-1.000,2379.640
2015-07-08,2046.680,0.226,-1.665,-1.067,-1.480,-2.646,1.024,1.001,0.540,1.000,4.630,1.000,2344.980
2015-07-09,2051.310,1.234,0.226,-1.443,-1.226,-2.388,1.020,1.007,0.526,1.000,25.310,1.000,2349.610
2015-07-10,2076.620,1.107,1.234,1.463,0.380,0.922,1.008,1.019,0.476,-1.000,22.980,1.000,2374.920
2015-07-13,2099.600,0.445,1.107,2.354,0.877,1.769,1.005,1.033,0.465,-1.000,9.350,1.000,2351.940
2015-07-14,2108.950,-0.073,0.445,1.557,3.042,1.518,1.008,1.041,0.488,-1.000,-1.550,-1.000,2342.590
2015-07-15,2107.400,0.801,-0.073,0.371,2.734,1.474,1.012,1.031,0.500,1.000,16.890,1.000,2344.140
2015-07-16,2124.290,0.111,0.801,0.727,2.296,2.684,1.005,1.025,0.479,-1.000,2.350,1.000,2361.030
2015-07-17,2126.640,0.077,0.111,0.913,1.288,2.176,1.004,1.016,0.483,-1.000,1.640,1.000,2358.680
2015-07-20,2128.280,-0.426,0.077,0.188,0.917,3.987,1.004,1.013,0.472,-1.000,-9.070,-1.000,2357.040
2015-07-21,2119.210,-0.239,-0.426,-0.349,0.560,3.310,1.008,1.006,0.484,-1.000,-5.060,-1.000,2366.110
2015-07-22,2114.150,-0.568,-0.239,-0.664,-0.477,1.807,1.009,1.001,0.493,-1.000,-12.000,-1.000,2371.170
2015-07-23,2102.150,-1.070,-0.568,-0.805,-1.152,0.121,1.017,1.001,0.507,1.000,-22.500,-1.000,2383.170
2015-07-24,2079.650,-0.578,-1.070,-1.632,-2.285,-1.389,1.030,1.002,0.525,1.000,-12.010,-1.000,2360.670
2015-07-27,2067.640,1.239,-0.578,-1.642,-2.433,-1.887,1.033,1.005,0.524,1.000,25.610,1.000,2348.660
2015-07-28,2093.250,0.732,1.239,0.654,-0.989,-1.461,1.015,1.017,0.495,-1.000,15.320,1.000,2374.270
2015-07-29,2108.570,0.003,0.732,1.980,0.305,-0.850,1.005,1.023,0.490,-1.000,0.060,1.000,2358.950
2015-07-30,2108.630,-0.227,0.003,0.735,1.394,-0.923,1.007,1.024,0.511,1.000,-4.790,-1.000,2358.890
2015-07-31,2103.840,-0.276,-0.227,-0.224,1.751,-0.725,1.011,1.019,0.520,1.000,-5.800,-1.000,2354.100
2015-08-03,2098.040,-0.225,-0.276,-0.502,0.229,-0.762,1.008,1.004,0.516,1.000,-4.720,-1.000,2348.300
2015-08-04,2093.320,0.311,-0.225,-0.500,-0.723,-0.420,1.010,1.001,0.509,1.000,6.520,1.000,2343.580
2015-08-05,2099.840,-0.775,0.311,0.086,-0.417,0.971,1.005,1.005,0.490,-1.000,-16.280,-1.000,2350.100
2015-08-06,2083.560,-0.287,-0.775,-0.466,-0.964,0.770,1.013,1.001,0.502,1.000,-5.990,-1.000,2366.380
2015-08-07,2077.570,1.281,-0.287,-1.061,-0.976,-0.749,1.014,1.002,0.515,1.000,26.610,1.000,2360.390
2015-08-10,2104.180,-0.956,1.281,0.990,0.519,-0.208,1.003,1.016,0.489,-1.000,-20.110,-1.000,2387.000
2015-08-11,2084.070,0.095,-0.956,0.313,-0.751,-1.165,1.013,1.007,0.515,1.000,1.980,1.000,2407.110
2015-08-12,2086.050,-0.128,0.095,-0.862,0.120,-0.846,1.009,1.008,0.516,1.000,-2.660,-1.000,2409.090
2015-08-13,2083.390,0.391,-0.128,-0.033,0.280,-0.698,1.010,1.007,0.511,1.000,8.150,1.000,2406.430
2015-08-14,2091.540,0.521,0.391,0.263,-0.601,-0.085,1.007,1.008,0.496,-1.000,10.900,1.000,2414.580
2015-08-17,2102.440,-0.263,0.521,0.914,0.881,0.124,1.001,1.013,0.495,-1.000,-5.520,-1.000,2403.680
2015-08-18,2096.920,-0.825,-0.263,0.257,0.521,0.641,1.004,1.009,0.500,1.000,-17.310,-1.000,2409.200
2015-08-19,2079.610,-2.110,-0.825,-1.086,-0.181,0.098,1.013,1.003,0.516,1.000,-43.880,-1.000,2391.890
2015-08-20,2035.730,-3.185,-2.110,-2.918,-2.668,-3.253,1.046,1.001,0.556,1.000,-64.840,-1.000,2348.010
2015-08-21,1970.890,-3.941,-3.185,-5.228,-6.257,-5.431,1.094,1.006,0.582,1.000,-77.680,-1.000,2283.170
2015-08-24,1893.210,-1.352,-3.941,-7.001,-9.715,-9.244,1.144,1.015,0.615,1.000,-25.600,-1.000,2205.490
2015-08-25,1867.610,3.903,-1.352,-5.240,-10.194,-10.357,1.141,1.033,0.588,1.000,72.900,1.000,2179.890
2015-08-26,1940.510,2.430,3.903,2.498,-4.677,-7.221,1.062,1.064,0.489,-1.000,47.150,1.000,2252.790
2015-08-27,1987.660,0.061,2.430,6.428,0.851,-5.459,1.018,1.080,0.483,-1.000,1.210,1.000,2205.640
2015-08-28,1988.870,-0.839,0.061,2.492,5.053,-5.153,1.022,1.082,0.549,1.000,-16.690,-1.000,2204.430
2015-08-31,1972.180,-2.958,-0.839,-0.779,5.599,-5.166,1.035,1.060,0.582,1.000,-58.330,-1.000,2187.740
2015-09-01,1913.850,1.829,-2.958,-3.772,-1.374,-5.987,1.055,1.006,0.597,1.000,35.010,1.000,2129.410
2015-09-02,1948.860,0.116,1.829,-1.182,-1.952,-1.118,1.036,1.023,0.496,-1.000,2.270,1.000,2164.420
2015-09-03,1951.130,-1.533,0.116,1.948,-1.898,3.059,1.028,1.024,0.453,-1.000,-29.910,-1.000,2162.150
2015-09-04,1921.220,2.508,-1.533,-1.418,-2.584,2.871,1.033,1.012,0.490,-1.000,48.190,1.000,2192.060
2015-09-08,1969.410,-1.390,2.508,0.937,2.903,1.489,1.006,1.037,0.475,-1.000,-27.370,-1.000,2143.870
2015-09-09,1942.040,0.528,-1.390,1.084,-0.350,-2.295,1.018,1.014,0.524,1.000,10.250,1.000,2171.240
2015-09-10,1952.290,0.449,0.528,-0.869,0.059,-1.839,1.014,1.019,0.520,1.000,8.760,1.000,2181.490
2015-09-11,1961.050,-0.409,0.449,0.979,2.073,-0.564,1.011,1.024,0.506,1.000,-8.020,-1.000,2190.250
2015-09-14,1953.030,1.283,-0.409,0.038,-0.832,2.047,1.011,1.008,0.485,-1.000,25.060,1.000,2182.230
2015-09-15,1978.090,0.871,1.283,0.869,1.856,1.500,1.002,1.023,0.482,-1.000,17.220,1.000,2157.170
2015-09-16,1995.310,-0.256,0.871,2.165,2.204,2.264,1.003,1.031,0.471,-1.000,-5.110,-1.000,2139.950
2015-09-17,1990.200,-1.616,-0.256,0.612,1.486,3.590,1.009,1.024,0.477,-1.000,-32.170,-1.000,2145.060
2015-09-18,1958.030,0.457,-1.616,-1.868,0.256,-0.578,1.026,1.009,0.536,1.000,8.940,1.000,2177.230
2015-09-21,1966.970,-1.232,0.457,-1.167,-0.562,1.284,1.020,1.009,0.494,-1.000,-24.230,-1.000,2186.170
2015-09-22,1942.740,-0.205,-1.232,-0.781,-2.635,-0.489,1.035,1.006,0.510,1.000,-3.980,-1.000,2210.400
2015-09-23,1938.760,-0.336,-0.205,-1.434,-2.585,-1.137,1.030,1.009,0.512,1.000,-6.520,-1.000,2206.420
2015-09-24,1932.240,-0.047,-0.336,-0.540,-1.317,-1.065,1.021,1.005,0.512,1.000,-0.900,-1.000,2199.900
2015-09-25,1931.340,-2.567,-0.047,-0.383,-1.811,-2.363,1.019,1.008,0.517,1.000,-49.570,-1.000,2199.000
2015-09-28,1881.770,0.123,-2.567,-2.612,-3.138,-5.690,1.047,1.000,0.575,1.000,2.320,1.000,2149.430
2015-09-29,1884.090,1.908,0.123,-2.446,-2.820,-5.332,1.042,1.011,0.551,1.000,35.940,1.000,2151.750
2015-09-30,1920.030,0.197,1.908,2.033,-0.632,-1.941,1.018,1.030,0.485,-1.000,3.790,1.000,2187.690
2015-10-01,1923.820,1.432,0.197,2.109,-0.389,-2.194,1.014,1.031,0.502,1.000,27.540,1.000,2183.900
2015-10-02,1951.360,1.829,1.432,1.632,3.698,0.444,1.007,1.049,0.491,-1.000,35.690,1.000,2211.440
2015-10-05,1987.050,-0.359,1.829,3.287,5.465,2.491,1.008,1.066,0.467,-1.000,-7.130,-1.000,2175.750
2015-10-06,1979.920,0.804,-0.359,1.464,3.119,2.468,1.014,1.044,0.488,-1.000,15.910,1.000,2182.880
2015-10-07,1995.830,0.882,0.804,0.442,3.743,3.339,1.012,1.042,0.481,-1.000,17.600,1.000,2166.970
2015-10-08,2013.430,0.073,0.882,1.692,3.181,6.997,1.006,1.035,0.439,-1.000,1.460,1.000,2149.370
2015-10-09,2014.890,0.128,0.073,0.955,1.401,6.942,1.006,1.023,0.444,-1.000,2.570,1.000,2147.910
2015-10-12,2017.460,-0.683,0.128,0.200,1.896,5.074,1.008,1.021,0.466,-1.000,-13.770,-1.000,2145.340
2015-10-13,2003.690,-0.472,-0.683,-0.556,0.394,4.152,1.011,1.005,0.480,-1.000,-9.450,-1.000,2159.110
2015-10-14,1994.240,1.485,-0.472,-1.151,-0.953,2.197,1.016,1.001,0.492,-1.000,29.620,1.000,2168.560
2015-10-15,2023.860,0.457,1.485,1.007,0.445,1.852,1.004,1.017,0.470,-1.000,9.250,1.000,2138.940
2015-10-16,2033.110,0.027,0.457,1.949,0.776,2.686,1.005,1.023,0.466,-1.000,0.550,1.000,2129.690
2015-10-19,2033.660,-0.142,0.027,0.484,1.496,1.895,1.008,1.024,0.490,-1.000,-2.890,-1.000,2129.140
2015-10-20,2030.770,-0.583,-0.142,-0.115,1.832,0.861,1.011,1.019,0.506,1.000,-11.830,-1.000,2132.030
2015-10-21,2018.940,1.663,-0.583,-0.724,-0.243,0.201,1.010,1.001,0.510,1.000,33.570,1.000,2120.200
2015-10-22,2052.510,1.103,1.663,1.071,0.954,1.737,1.001,1.020,0.471,-1.000,22.640,1.000,2153.770
2015-10-23,2075.150,-0.191,1.103,2.784,2.040,3.566,1.003,1.036,0.452,-1.000,-3.970,-1.000,2131.130
2015-10-26,2071.180,-0.255,-0.191,0.910,1.990,3.858,1.011,1.033,0.474,-1.000,-5.290,-1.000,2135.100
2015-10-27,2065.890,1.184,-0.255,-0.446,2.325,2.077,1.015,1.025,0.501,1.000,24.460,1.000,2140.390
2015-10-28,2090.350,-0.045,1.184,0.926,1.844,2.815,1.003,1.021,0.471,-1.000,-0.940,-1.000,2164.850
2015-10-29,2089.410,-0.481,-0.045,1.138,0.687,2.741,1.004,1.015,0.475,-1.000,-10.050,-1.000,2165.790
2015-10-30,2079.360,1.187,-0.481,-0.526,0.395,2.393,1.009,1.009,0.492,-1.000,24.690,1.000,2175.840
2015-11-02,2104.050,0.273,1.187,0.701,1.847,4.216,1.003,1.021,0.461,-1.000,5.740,1.000,2151.150
2015-11-03,2109.790,-0.355,0.273,1.463,0.930,2.791,1.003,1.018,0.471,-1.000,-7.480,-1.000,2145.410
2015-11-04,2102.310,-0.113,-0.355,-0.083,0.617,1.309,1.008,1.013,0.498,-1.000,-2.380,-1.000,2152.890
2015-11-05,2099.930,-0.035,-0.113,-0.467,0.989,1.388,1.009,1.010,0.500,1.000,-0.730,-1.000,2155.270
2015-11-06,2099.200,-0.982,-0.035,-0.148,-0.231,1.612,1.005,1.002,0.490,-1.000,-20.620,-1.000,2154.540
2015-11-09,2078.580,0.151,-0.982,-1.017,-1.479,-0.563,1.019,1.001,0.516,1.000,3.140,1.000,2175.160
2015-11-10,2081.720,-0.323,0.151,-0.833,-0.979,-0.368,1.015,1.005,0.506,1.000,-6.720,-1.000,2178.300
2015-11-11,2075.000,-1.399,-0.323,-0.172,-1.187,-0.210,1.016,1.004,0.503,1.000,-29.030,-1.000,2171.580
2015-11-12,2045.970,-1.121,-1.399,-1.717,-2.536,-2.760,1.032,1.002,0.538,1.000,-22.930,-1.000,2142.550
2015-11-13,2023.040,1.490,-1.121,-2.504,-2.672,-4.112,1.041,1.004,0.552,1.000,30.150,1.000,2119.620
2015-11-16,2053.190,-0.134,1.490,0.353,-1.371,-2.336,1.022,1.020,0.501,1.000,-2.750,-1.000,2149.770
2015-11-17,2050.440,1.616,-0.134,1.354,-1.184,-2.357,1.016,1.017,0.508,1.000,33.140,1.000,2147.020
2015-11-18,2083.580,-0.112,1.616,1.480,1.838,-0.744,1.003,1.035,0.493,-1.000,-2.340,-1.000,2180.160
2015-11-19,2081.240,0.381,-0.112,1.502,2.877,0.128,1.010,1.034,0.504,1.000,7.930,1.000,2182.500
2015-11-20,2089.170,-0.123,0.381,0.268,1.752,0.358,1.007,1.025,0.502,1.000,-2.580,-1.000,2190.430
2015-11-23,2086.590,0.122,-0.123,0.257,1.763,0.559,1.010,1.018,0.505,1.000,2.550,1.000,2187.850
2015-11-24,2089.140,-0.013,0.122,-0.001,0.267,2.110,1.001,1.005,0.486,-1.000,-0.270,-1.000,2190.400
2015-11-25,2088.870,0.059,-0.013,0.109,0.367,3.254,1.002,1.004,0.477,-1.000,1.240,1.000,2190.670
2015-11-27,2090.110,-0.464,0.059,0.046,0.045,1.798,1.000,1.002,0.487,-1.000,-9.700,-1.000,2189.430
2015-11-30,2080.410,1.068,-0.464,-0.405,-0.296,1.462,1.007,1.000,0.496,-1.000,22.220,1.000,2199.130
2015-12-01,2102.630,-1.100,1.068,0.599,0.646,0.914,1.001,1.013,0.485,-1.000,-23.120,-1.000,2176.910
2015-12-02,2079.510,-1.437,-1.100,-0.043,-0.448,-0.083,1.012,1.004,0.511,1.000,-29.890,-1.000,2200.030
2015-12-03,2049.620,2.053,-1.437,-2.521,-1.937,-1.893,1.032,1.002,0.540,1.000,42.070,1.000,2170.140
2015-12-04,2091.690,-0.699,2.053,0.586,0.542,0.244,1.012,1.023,0.482,-1.000,-14.620,-1.000,2212.210
2015-12-07,2077.070,-0.649,-0.699,1.339,-1.216,-0.578,1.019,1.016,0.498,-1.000,-13.480,-1.000,2226.830
2015-12-08,2063.590,-0.774,-0.649,-1.343,-0.766,-1.210,1.018,1.010,0.524,1.000,-15.970,-1.000,2240.310
2015-12-09,2047.620,0.225,-0.774,-1.418,-0.098,-2.033,1.025,1.007,0.536,1.000,4.610,1.000,2224.340
2015-12-10,2052.230,-1.942,0.225,-0.551,-1.887,-1.355,1.023,1.009,0.507,1.000,-39.860,-1.000,2228.950
2015-12-11,2012.370,0.476,-1.942,-1.722,-3.115,-4.293,1.040,1.003,0.553,1.000,9.570,1.000,2189.090
2015-12-14,2021.940,1.062,0.476,-1.476,-2.018,-2.768,1.028,1.010,0.523,1.000,21.470,1.000,2198.660
2015-12-15,2043.410,1.451,1.062,1.542,-0.206,-0.303,1.011,1.019,0.484,-1.000,29.660,1.000,2220.130
2015-12-16,2073.070,-1.504,1.451,2.529,1.015,-0.890,1.005,1.038,0.484,-1.000,-31.180,-1.000,2190.470
2015-12-17,2041.890,-1.780,-1.504,-0.074,1.467,-1.694,1.019,1.023,0.536,1.000,-36.340,-1.000,2221.650
2015-12-18,2005.550,0.778,-1.780,-3.257,-0.811,-2.813,1.038,1.007,0.561,1.000,15.600,1.000,2185.310
2015-12-21,2021.150,0.882,0.778,-1.016,-1.089,-1.293,1.030,1.015,0.509,1.000,17.820,1.000,2200.910
2015-12-22,2038.970,1.242,0.882,1.666,-1.645,-0.646,1.021,1.024,0.480,-1.000,25.320,1.000,2218.730
2015-12-23,2064.290,-0.160,1.242,2.134,1.097,2.580,1.005,1.035,0.460,-1.000,-3.300,-1.000,2193.410
2015-12-24,2060.990,-0.218,-0.160,1.080,2.764,1.931,1.011,1.034,0.492,-1.000,-4.490,-1.000,2196.710
2015-12-28,2056.500,1.063,-0.218,-0.377,1.749,0.641,1.012,1.020,0.510,1.000,21.860,1.000,2201.200
2015-12-29,2078.360,-0.722,1.063,0.843,1.932,0.255,1.003,1.022,0.494,-1.000,-15.000,-1.000,2223.060
2015-12-30,2063.360,-0.941,-0.722,0.334,-0.045,1.051,1.008,1.007,0.497,-1.000,-19.420,-1.000,2238.060
2015-12-31,2043.940,-1.530,-0.941,-1.656,-0.827,1.914,1.019,1.003,0.503,1.000,0.000,0.000,2257.480
Next, I use the above artifact and matplotlib to create the Blue-Line Green-Line Visualization:

# plotem.py

# This script should plot data in a CSV file

# Demo:
# cd ~/ddata
# python ~/pyspy/py/plotem.py learn_test.csv

import pandas as pd
import numpy  as np
import pdb
import datetime

# I should check cmd line arg
import sys

print('hello, from '+ sys.argv[0])

#  len(sys.argv) should == 2
if len(sys.argv) == 1:
  print('I need a command line arg.')
  print('Demo:')
  print('python '+sys.argv[0]+' learn_test.csv')
  print('Try again. bye.')
  sys.exit()

csvf = sys.argv[1]
print('Plotting data from: '+csvf)

# I should load the csv into a DataFrame
df1 = pd.read_csv(csvf)

# matplotlib likes dates:
cdate_l = [datetime.datetime.strptime(row, "%Y-%m-%d") for row in df1['cdate'].values]
# I should get values for y-axis now:
cp_l = [row for row in df1['cp']       ] 
gl_l = [row for row in df1['greenline']] 

# I should plot
import matplotlib
# http://matplotlib.org/faq/howto_faq.html#generate-images-without-having-a-window-appear
matplotlib.use('Agg')
# Order is important here.
# Do not move the next import:
import matplotlib.pyplot as plt
plt.figure(figsize=(15,10)) # 10inch x 5inch
plt.plot(cdate_l, cp_l, 'b-', cdate_l, gl_l, 'g-')
plt.title('Blue-Line/Green-Line Visualization (Blue: Long Only, Green: Follow Predictions)')
plt.grid(True)
pngf = csvf.replace('.csv','')+'.png'
plt.savefig(pngf)
plt.close()
print('New png file: ')
print(pngf)
'done'
The above script should give me an image file which looks like this:

Build the ML Application with Django

The easy way to build the ML Application with Django is to git clone my repository into your development environment.

On my Linux laptop I started by running two shell commands:
cd ~
git clone https://github.com/danbikle/herokuspy
Next, I installed the Heroku Toolbelt:
cd ~
rm -rf heroku-client.tgz heroku-client
wget https://s3.amazonaws.com/assets.heroku.com/heroku-client/heroku-client.tgz
tar zxf heroku-client.tgz
echo 'export PATH=/home/ann/heroku-client/bin:${PATH}' >> ~/.bashrc
bash
Then, I create an ssh-key for my account; I had none yet:
ssh-keygen -t rsa
Next, I browsed heroku.com and created an account via the web-ui. Then, I returned to the shell and gave a copy of public ssh-key to heroku:
cd ~/herokuspy
~/heroku-client/bin/heroku status
~/heroku-client/bin/heroku auth:login
~/heroku-client/bin/heroku auth:whoami
~/heroku-client/bin/heroku keys:add
Also, on my Linux laptop, I created an ann role in Postgres:
sudo su - postgres
psql
CREATE ROLE ann WITH superuser login;
CREATE DATABASE ann;
Above, I picked the name 'ann' because that name matched the name of my Linux account.

I ran some shell commands:
cd ~/herokuspy
~/anaconda3/bin/pip install -r requirements.txt
/usr/bin/createdb python_getting_started
~/heroku-client/bin/heroku local:run python manage.py migrate
python manage.py collectstatic
~/heroku-client/bin/heroku local
My app should now be accepting requests at this URL:

http://localhost:5000

Deploy the ML Application to Heroku

I created an app on heroku:
cd ~/herokuspy
~/heroku-client/bin/heroku create hspy10
You will need to use a different app name than hspy10.

Perhaps hspy11 is available? I deployed my app to heroku with two shell commands:
git push heroku master
~/heroku-client/bin/heroku run python manage.py migrate
If heroku likes you and your laptop it will tell you the URL which is serving your app.

I see my app at this URL:

https://hspy10.herokuapp.com

Operate the ML Application on Heroku

I operate herokuspy in two different modes.

First, I run a script at 12:50pm Mon-Fri California time which relies on pyspy to observe the current price of the S&P 500 index.

Next, the script uses this new observation to generate the most recent prediction.

This prediction is published to the web so that users can act on it before the market closes at 13:00pm (16:00 NY time).

The script is listed below:
~/herokuspy/bin/noon50.bash
In the second mode I run a script at 21:00pm Mon-Fri California time which uses the closing price of the S&P 500 index to generate the most recent prediction.

This second script is listed below:
~/herokuspy/bin/night.bash
I see the scripts listed on github too:

https://github.com/danbikle/herokuspy/blob/master/bin/noon50.bash

https://github.com/danbikle/herokuspy/blob/master/bin/night.bash

If you want to run herokuspy on your laptop and you want help, e-me:

bikle101@gmail.com


syntax.us Let the syntax do the talking
Blog Contact Posts Questions Tags Hire Me