# Ranking of articles using open-access citation-metadata

Scholarly publications have seen exponential growth in past decades, however, linear growth is estimated for the research topics. It suggests that numerous articles appear every day within a research field. Researchers invest their time and energy in keeping up-to-date with the current state-of-the-art. Research is a continuous process and it builds on the past work that has been done attributed through citations. Although, there are numerous reasons why a research article gets cited, as well as, its critics as to why citations should not be used to assess the value of current work. However, with the current information overload, it is not easy to keep abreast of all the published work. Researchers in 20th century would dig through all the available literature to find out the latest trends but the researcher of today has more stuff to read on a topic than their lifetime. They need access to current research as soon as it happens but the citation-count metrics, currently in practice, limit this approach. To use citation-based metrics, the articles must acquire a reasonable number of citations which can vary from field to field. Our main contribution is to use a heterogeneous network that includes the article, author and journal to recommend articles in a research field.

# Import

```python
import Ranking # from https://github.com/bilal-dsu/Ranking/

from matplotlib import pyplot
from scipy.stats import spearmanr
import json, os,sys,snap
import csv
import pandas as pd
import snap
import numpy as np
import re
from itertools import combinations
from os import path
import seaborn as sns
import matplotlib.pyplot as plt

import measures # from https://github.com/dkaterenchuk/ranking_measures

```

# Initializations Total Citations

The original graph is filtered to work with nodes between the year 2000 till 2018, termed Total Citations (TC). Further, we remove any journals with zero out-degree since they do not participate in the ranking method.

```python
# following files must be present in the CWD
metaDataCSV = "MetaData 2000-2018.csv"
ArticleGraph = "ArticleGraph.graph"
ArticleHash = "ArticleHash.hash"

# following files will be created in the CWD
JournalCitationTXT = "JournalCitation.txt"
JournalGraph = "JournalGraph.graph"
JournalHash = "JournalHash.hash"
JournalSubGraph = "JournalSubGraph.graph"
SubMetaDataCSV = "SubMetaData.csv"
AuthorCitationTXT = "AuthorCitation.txt"
ArticleCitationTXT = "ArticleCitation.txt"
AuthorGraph = "AuthorGraph.graph"
AuthorHash = "AuthorHash.hash"

AuthorInfoCSV = "AuthorInfo.csv"
JournalInfoCSV = "JournalInfo.csv"
ArticleInfoCSV = "ArticleInfo.csv"

AuthorRankCSV = "AuthorRank.csv"
JournalRankCSV = "JournalRank.csv"
ArticleRankCSV = "ArticleRank.csv"

ArticlesGraphStats="ArticleGraphStats.csv"
JournalGraphStats="JournalGraphStats.csv"
AuthorGraphStats="AuthorGraphStats.csv"
GraphStatsOverall="GraphStatsOverall.csv"
```

# Generate Total Citations

```python
Ranking.generateJournanalCitationNetworkText(metaDataCSV, JournalCitationTXT)
```

```python
Ranking.generateJournalCitationGraph(JournalCitationTXT, JournalGraph, JournalHash)
```

```python
Ranking.generateSubGraph(JournalHash, JournalGraph, JournalSubGraph, metaDataCSV, SubMetaDataCSV)
```

```python
Ranking.generateAuthorArticleCitationNetworkText(SubMetaDataCSV, AuthorCitationTXT, ArticleCitationTXT)
```

```python
Ranking.generateAuthorArticleGraph(AuthorCitationTXT, AuthorGraph, AuthorHash, ArticleCitationTXT,
                           ArticleGraph, ArticleHash)
```

# Initializations Early Citations

To evaluate the ranking technique, we take nodes of the year 2005 and apply a cut-off on citations till 2010, termed Early Citations (EC).  The cutoff window is configurable. Only a few past years are considered to give equal chance to early career researchers.

```python
# Provide values for Early Citations cutoff

RankYearStart = 2005
RankYearEnd = 2005
CutOffStart = 2000
CutOffEnd = 2010

# following files will be created in the CWD

metaDataRankYearCSV = "metaData" + str(RankYearStart)  + "-" + str(RankYearEnd) + ".csv"
metaDataCutOffYearCSV = "metaData" + str(CutOffStart)  + "-" + str(CutOffEnd) + ".csv"

JournalCutOffYearTXT = "Journal" + str(CutOffStart)  + "-" + str(CutOffEnd) + ".txt"
JournalCutOffYearGraph = "Journal" + str(CutOffStart)  + "-" + str(CutOffEnd) + ".graph"
JournalCutOffYearHash = "Journal" + str(CutOffStart)  + "-" + str(CutOffEnd) + ".hash"
JournalCutOffYearInfoCSV = "Journal" + str(CutOffStart)  + "-" + str(CutOffEnd) + "Info.csv"
JournalCutOffYearRankCSV = "Journal" + str(CutOffStart)  + "-" + str(CutOffEnd) + "Rank.csv"

JournalCutOffYearSubGraph = "JournalSubGraph"+ str(CutOffStart)  + "-" + str(CutOffEnd) + ".graph"

ArticleCutOffYearTXT = "Article" + str(CutOffStart)  + "-" + str(CutOffEnd) + ".txt"
ArticleCutOffYearGraph = "Article" + str(CutOffStart)  + "-" + str(CutOffEnd) + ".graph"
ArticleCutOffYearHash = "Article" + str(CutOffStart)  + "-" + str(CutOffEnd) + ".hash"
ArticleCutOffYearInfoCSV = "Article" + str(CutOffStart)  + "-" + str(CutOffEnd) + "Info.csv"
ArticleCutOffYearRankCSV = "Article" + str(CutOffStart)  + "-" + str(CutOffEnd) + "Rank.csv"

AuthorCutOffYearTXT = "Author" + str(CutOffStart)  + "-" + str(CutOffEnd) + ".txt"
AuthorCutOffYearGraph = "Author" + str(CutOffStart)  + "-" + str(CutOffEnd) + ".graph"
AuthorCutOffYearHash = "Author" + str(CutOffStart)  + "-" + str(CutOffEnd) + ".hash"
AuthorCutOffYearInfoCSV = "Author" + str(CutOffStart)  + "-" + str(CutOffEnd) + "Info.csv"
AuthorCutOffYearRankCSV = "Author" + str(CutOffStart)  + "-" + str(CutOffEnd) + "Rank.csv"

AuthorGraphStatsCutOffYear = "AuthorGraphStats" + str(CutOffStart)  + "-" + str(CutOffEnd) + ".csv"
ArticleGraphStatsCutOffYear = "ArticleGraphStats" + str(CutOffStart)  + "-" + str(CutOffEnd) + ".csv"
JournalGraphStatsCutOffYear = "JournalGraphStats" + str(CutOffStart)  + "-" + str(CutOffEnd) + ".csv"
GraphStatsCutOffYear = "GraphStats" + str(CutOffStart)  + "-" + str(CutOffEnd) + ".csv"
```

# Generate Early Citations

```python
Ranking.generateTemporalNetwork(SubMetaDataCSV, RankYearStart,RankYearEnd, CutOffStart, CutOffEnd,
                        metaDataRankYearCSV, metaDataCutOffYearCSV, ArticleHash, ArticleGraph)
```

```python
Ranking.generateJournanalCitationNetworkText(metaDataCutOffYearCSV, JournalCutOffYearTXT)
```

```python
Ranking.generateJournalCitationGraph(JournalCutOffYearTXT, JournalCutOffYearGraph, JournalCutOffYearHash)
```

```python
Ranking.generateAuthorArticleCitationNetworkText(metaDataCutOffYearCSV, AuthorCutOffYearTXT, ArticleCutOffYearTXT)
```

```python
Ranking.generateAuthorArticleGraph(AuthorCutOffYearTXT, AuthorCutOffYearGraph, AuthorCutOffYearHash,
                           ArticleCutOffYearTXT, ArticleCutOffYearGraph, ArticleCutOffYearHash)
```

# Calculate Rank

The rank of a journal or author is given by the PageRank measure in Equation 1.
\begin{equation}
\label{eq:Rank}
R(i) = ((1-\alpha)/n) + \alpha * \sum_{\substack{j}} R(j)\frac{aij}{Ni}
\end{equation}
where, n is the total no. of nodes in the network,

$\alpha$ $\epsilon$ $(0 , 1)$ is damping factor (set to $0.85$),

aij is 1 if node (i) cites node (j) and 0 otherwise,

Ni is the total neighbours of node i.

The rank of journal and author is transferred to the article given by Equation 2, thereby, inflating the rank of the article which was cited by any influential journal or author. The rank of the article “a” published in journal “b” by the author(s) “c” is:
\begin{equation} \label{eq:ArticleRank}
AR(a) = ((1-\beta) * R(b) + \beta * \frac {\sum_{\substack{i}} R (i)}{c})
\end{equation}
where, $\beta$ $\epsilon$ $(0 , 1)$ is adjustment for weight-age of author and journal influence (set to $0.5$).

```python
Ranking.generateAuthorJournalRank(AuthorHash, AuthorGraph, AuthorInfoCSV, JournalHash, JournalSubGraph, JournalInfoCSV, JournalGraphStats, AuthorGraphStats)
```

```python
Ranking.generateArticleRank(JournalInfoCSV, SubMetaDataCSV, ArticleGraph, ArticleHash, AuthorInfoCSV, ArticleInfoCSV, ArticlesGraphStats)
```

```python
Ranking.generateAuthorJournalRank(AuthorCutOffYearHash, AuthorCutOffYearGraph, AuthorCutOffYearInfoCSV,
             JournalCutOffYearHash, JournalCutOffYearGraph, JournalCutOffYearInfoCSV, JournalGraphStatsCutOffYear,
                           AuthorGraphStatsCutOffYear)
```

```python
Ranking.generateArticleRank(JournalCutOffYearInfoCSV, metaDataCutOffYearCSV, ArticleCutOffYearGraph, ArticleCutOffYearHash,
                    AuthorCutOffYearInfoCSV, ArticleCutOffYearInfoCSV, ArticleGraphStatsCutOffYear)
```

# Analysis

On the temporal citation network, we correlate the EC rank of publications with the rank calculated using TC. It is used as a baseline for evaluating the ranking mechanism. To identify whether our technique captures key articles with a high EC rank that went on to attain a high rank in TC, we apply Spearman's rank correlation. Our preliminary analysis suggests that the ranking technique is stable. The rank calculated with EC correlates with rank calculated with TC. However, there is no significant correlation with citation count, suggesting that the technique does not rely on merely counting citations. It essentially means that instead of only counting citations the value of a citation coming from a reputable journal gets a higher rank.

```python
Ranking.generateGraphStats(JournalGraphStats, AuthorGraphStats, ArticlesGraphStats, GraphStatsOverall)
```

```python
Ranking.generateGraphStats(JournalGraphStatsCutOffYear, AuthorGraphStatsCutOffYear,
                   ArticleGraphStatsCutOffYear , GraphStatsCutOffYear)
```

```python
Ranking.correlationAnalysis(AuthorInfoCSV, AuthorCutOffYearInfoCSV, JournalInfoCSV,
            JournalCutOffYearInfoCSV, ArticleInfoCSV, ArticleCutOffYearInfoCSV, metaDataRankYearCSV)

```


# Running TensorTrade on SageMaker Studio
This notebook demonstrates running TensorTrade on SageMaker Studio. EOD data for any ETF/stock is fetched from [Yahoo Finance](https://finance.yahoo.com/) or S3 bucket. Experimental evidence shows that running on ml.g4dn.xlarge instance completes sooner than running on ml.t3.medium instance.

```python
!pip install pip --upgrade
!pip install git+https://github.com/msrparadesi/tensortrade.git
!pip install git+https://github.com/msrparadesi/tensortrade.git#egg=tensortrade[tf,tensorforce,baselines,ccxt,ta,fbm] -U
!pip install yfinance ta matplotlib s3fs
```

    Collecting pip
      Using cached pip-20.1.1-py2.py3-none-any.whl (1.5 MB)
    Installing collected packages: pip
      Attempting uninstall: pip
        Found existing installation: pip 20.0.2
        Uninstalling pip-20.0.2:
          Successfully uninstalled pip-20.0.2
    Successfully installed pip-20.1.1
    Collecting git+https://github.com/msrparadesi/tensortrade.git
      Cloning https://github.com/msrparadesi/tensortrade.git to /tmp/pip-req-build-i8sut2x8
      Running command git clone -q https://github.com/msrparadesi/tensortrade.git /tmp/pip-req-build-i8sut2x8
    Requirement already satisfied: numpy>=1.16.4 in /usr/local/lib/python3.6/dist-packages (from tensortrade==0.2.0b2) (1.18.2)
    Collecting pandas>=0.25.0
      Using cached pandas-1.0.5-cp36-cp36m-manylinux1_x86_64.whl (10.1 MB)
    Processing ./.cache/pip/wheels/be/a1/84/6b4caa6c1cea703acbfea8a24cc3c1729bd359cd4a65755d8b/gym-0.17.2-py3-none-any.whl
    Requirement already satisfied: pyyaml>=5.1.2 in /usr/local/lib/python3.6/dist-packages (from tensortrade==0.2.0b2) (5.3.1)
    Collecting sympy>=1.4
      Using cached sympy-1.6.1-py3-none-any.whl (5.8 MB)
    Requirement already satisfied: pytz>=2017.2 in /usr/local/lib/python3.6/dist-packages (from pandas>=0.25.0->tensortrade==0.2.0b2) (2019.3)
    Requirement already satisfied: python-dateutil>=2.6.1 in /usr/local/lib/python3.6/dist-packages (from pandas>=0.25.0->tensortrade==0.2.0b2) (2.8.1)
    Collecting pyglet<=1.5.0,>=1.4.0
      Using cached pyglet-1.5.0-py2.py3-none-any.whl (1.0 MB)
    Requirement already satisfied: cloudpickle<1.4.0,>=1.2.0 in /usr/local/lib/python3.6/dist-packages (from gym>=0.14.0->tensortrade==0.2.0b2) (1.3.0)
    Requirement already satisfied: scipy in /usr/local/lib/python3.6/dist-packages (from gym>=0.14.0->tensortrade==0.2.0b2) (1.2.2)
    Processing ./.cache/pip/wheels/e8/38/69/aa17553ad31f09ff5fa44c8a1a6c5b47e7c9261e9c7c16b9fb/mpmath-1.1.0-py3-none-any.whl
    Requirement already satisfied: six>=1.5 in /usr/local/lib/python3.6/dist-packages (from python-dateutil>=2.6.1->pandas>=0.25.0->tensortrade==0.2.0b2) (1.14.0)
    Processing ./.cache/pip/wheels/6e/9c/ed/4499c9865ac1002697793e0ae05ba6be33553d098f3347fb94/future-0.18.2-py3-none-any.whl
    Building wheels for collected packages: tensortrade
      Building wheel for tensortrade (setup.py) ... done
      Created wheel for tensortrade: filename=tensortrade-0.2.0b2-py3-none-any.whl size=120451 sha256=dfb755daa86b34133e6d3f900081783e2f67e7e1b9e304c62ebb587680ad67ca
      Stored in directory: /tmp/pip-ephem-wheel-cache-cx0sif_b/wheels/7d/90/2a/bb771201465fd1314ad06c9290fdcc23ca3389e8b75ef54a32
    Successfully built tensortrade
    Installing collected packages: pandas, future, pyglet, gym, mpmath, sympy, tensortrade
      Attempting uninstall: pandas
        Found existing installation: pandas 0.24.2
        Uninstalling pandas-0.24.2:
          Successfully uninstalled pandas-0.24.2
    Successfully installed future-0.18.2 gym-0.17.2 mpmath-1.1.0 pandas-1.0.5 pyglet-1.5.0 sympy-1.6.1 tensortrade-0.2.0b2
    Collecting tensortrade[baselines,ccxt,fbm,ta,tensorforce,tf]
      Cloning https://github.com/msrparadesi/tensortrade.git to /tmp/pip-install-1s0vibmz/tensortrade
      Running command git clone -q https://github.com/msrparadesi/tensortrade.git /tmp/pip-install-1s0vibmz/tensortrade
      WARNING: tensortrade 0.2.0b2 does not provide the extra 'baselines'
      WARNING: tensortrade 0.2.0b2 does not provide the extra 'ccxt'
      WARNING: tensortrade 0.2.0b2 does not provide the extra 'fbm'
      WARNING: tensortrade 0.2.0b2 does not provide the extra 'ta'
      WARNING: tensortrade 0.2.0b2 does not provide the extra 'tensorforce'
    Requirement already satisfied, skipping upgrade: numpy>=1.16.4 in /usr/local/lib/python3.6/dist-packages (from tensortrade[baselines,ccxt,fbm,ta,tensorforce,tf]) (1.18.2)
    Requirement already satisfied, skipping upgrade: pandas>=0.25.0 in /usr/local/lib/python3.6/dist-packages (from tensortrade[baselines,ccxt,fbm,ta,tensorforce,tf]) (1.0.5)
    Requirement already satisfied, skipping upgrade: gym>=0.14.0 in /usr/local/lib/python3.6/dist-packages (from tensortrade[baselines,ccxt,fbm,ta,tensorforce,tf]) (0.17.2)
    Requirement already satisfied, skipping upgrade: pyyaml>=5.1.2 in /usr/local/lib/python3.6/dist-packages (from tensortrade[baselines,ccxt,fbm,ta,tensorforce,tf]) (5.3.1)
    Requirement already satisfied, skipping upgrade: sympy>=1.4 in /usr/local/lib/python3.6/dist-packages (from tensortrade[baselines,ccxt,fbm,ta,tensorforce,tf]) (1.6.1)
    Collecting tensorflow>=2.1.0
      Using cached tensorflow-2.2.0-cp36-cp36m-manylinux2010_x86_64.whl (516.2 MB)
    Requirement already satisfied, skipping upgrade: pytz>=2017.2 in /usr/local/lib/python3.6/dist-packages (from pandas>=0.25.0->tensortrade[baselines,ccxt,fbm,ta,tensorforce,tf]) (2019.3)
    Requirement already satisfied, skipping upgrade: python-dateutil>=2.6.1 in /usr/local/lib/python3.6/dist-packages (from pandas>=0.25.0->tensortrade[baselines,ccxt,fbm,ta,tensorforce,tf]) (2.8.1)
    Requirement already satisfied, skipping upgrade: scipy in /usr/local/lib/python3.6/dist-packages (from gym>=0.14.0->tensortrade[baselines,ccxt,fbm,ta,tensorforce,tf]) (1.2.2)
    Requirement already satisfied, skipping upgrade: cloudpickle<1.4.0,>=1.2.0 in /usr/local/lib/python3.6/dist-packages (from gym>=0.14.0->tensortrade[baselines,ccxt,fbm,ta,tensorforce,tf]) (1.3.0)
    Requirement already satisfied, skipping upgrade: pyglet<=1.5.0,>=1.4.0 in /usr/local/lib/python3.6/dist-packages (from gym>=0.14.0->tensortrade[baselines,ccxt,fbm,ta,tensorforce,tf]) (1.5.0)
    Requirement already satisfied, skipping upgrade: mpmath>=0.19 in /usr/local/lib/python3.6/dist-packages (from sympy>=1.4->tensortrade[baselines,ccxt,fbm,ta,tensorforce,tf]) (1.1.0)
    Requirement already satisfied, skipping upgrade: grpcio>=1.8.6 in /usr/local/lib/python3.6/dist-packages (from tensorflow>=2.1.0->tensortrade[baselines,ccxt,fbm,ta,tensorforce,tf]) (1.27.2)
    Collecting tensorflow-estimator<2.3.0,>=2.2.0
      Using cached tensorflow_estimator-2.2.0-py2.py3-none-any.whl (454 kB)
    Requirement already satisfied, skipping upgrade: six>=1.12.0 in /usr/local/lib/python3.6/dist-packages (from tensorflow>=2.1.0->tensortrade[baselines,ccxt,fbm,ta,tensorforce,tf]) (1.14.0)
    Requirement already satisfied, skipping upgrade: opt-einsum>=2.3.2 in /usr/local/lib/python3.6/dist-packages (from tensorflow>=2.1.0->tensortrade[baselines,ccxt,fbm,ta,tensorforce,tf]) (3.2.0)
    Requirement already satisfied, skipping upgrade: protobuf>=3.8.0 in /usr/local/lib/python3.6/dist-packages (from tensorflow>=2.1.0->tensortrade[baselines,ccxt,fbm,ta,tensorforce,tf]) (3.11.3)
    Requirement already satisfied, skipping upgrade: keras-preprocessing>=1.1.0 in /usr/local/lib/python3.6/dist-packages (from tensorflow>=2.1.0->tensortrade[baselines,ccxt,fbm,ta,tensorforce,tf]) (1.1.0)
    Collecting astunparse==1.6.3
      Using cached astunparse-1.6.3-py2.py3-none-any.whl (12 kB)
    Collecting gast==0.3.3
      Using cached gast-0.3.3-py2.py3-none-any.whl (9.7 kB)
    Requirement already satisfied, skipping upgrade: google-pasta>=0.1.8 in /usr/local/lib/python3.6/dist-packages (from tensorflow>=2.1.0->tensortrade[baselines,ccxt,fbm,ta,tensorforce,tf]) (0.2.0)
    Requirement already satisfied, skipping upgrade: wheel>=0.26; python_version >= "3" in /usr/local/lib/python3.6/dist-packages (from tensorflow>=2.1.0->tensortrade[baselines,ccxt,fbm,ta,tensorforce,tf]) (0.34.2)
    Requirement already satisfied, skipping upgrade: absl-py>=0.7.0 in /usr/local/lib/python3.6/dist-packages (from tensorflow>=2.1.0->tensortrade[baselines,ccxt,fbm,ta,tensorforce,tf]) (0.9.0)
    Requirement already satisfied, skipping upgrade: h5py<2.11.0,>=2.10.0 in /usr/local/lib/python3.6/dist-packages (from tensorflow>=2.1.0->tensortrade[baselines,ccxt,fbm,ta,tensorforce,tf]) (2.10.0)
    Requirement already satisfied, skipping upgrade: termcolor>=1.1.0 in /usr/local/lib/python3.6/dist-packages (from tensorflow>=2.1.0->tensortrade[baselines,ccxt,fbm,ta,tensorforce,tf]) (1.1.0)
    Collecting tensorboard<2.3.0,>=2.2.0
      Using cached tensorboard-2.2.2-py3-none-any.whl (3.0 MB)
    Requirement already satisfied, skipping upgrade: wrapt>=1.11.1 in /usr/local/lib/python3.6/dist-packages (from tensorflow>=2.1.0->tensortrade[baselines,ccxt,fbm,ta,tensorforce,tf]) (1.12.1)
    Requirement already satisfied, skipping upgrade: future in /usr/local/lib/python3.6/dist-packages (from pyglet<=1.5.0,>=1.4.0->gym>=0.14.0->tensortrade[baselines,ccxt,fbm,ta,tensorforce,tf]) (0.18.2)
    Requirement already satisfied, skipping upgrade: setuptools in /usr/local/lib/python3.6/dist-packages (from protobuf>=3.8.0->tensorflow>=2.1.0->tensortrade[baselines,ccxt,fbm,ta,tensorforce,tf]) (46.0.0)
    Requirement already satisfied, skipping upgrade: werkzeug>=0.11.15 in /usr/local/lib/python3.6/dist-packages (from tensorboard<2.3.0,>=2.2.0->tensorflow>=2.1.0->tensortrade[baselines,ccxt,fbm,ta,tensorforce,tf]) (0.15.5)
    Collecting google-auth<2,>=1.6.3
      Using cached google_auth-1.18.0-py2.py3-none-any.whl (90 kB)
    Collecting tensorboard-plugin-wit>=1.6.0
      Using cached tensorboard_plugin_wit-1.7.0-py3-none-any.whl (779 kB)
    Requirement already satisfied, skipping upgrade: markdown>=2.6.8 in /usr/local/lib/python3.6/dist-packages (from tensorboard<2.3.0,>=2.2.0->tensorflow>=2.1.0->tensortrade[baselines,ccxt,fbm,ta,tensorforce,tf]) (3.2.1)
    Requirement already satisfied, skipping upgrade: requests<3,>=2.21.0 in /usr/local/lib/python3.6/dist-packages (from tensorboard<2.3.0,>=2.2.0->tensorflow>=2.1.0->tensortrade[baselines,ccxt,fbm,ta,tensorforce,tf]) (2.22.0)
    Collecting google-auth-oauthlib<0.5,>=0.4.1
      Using cached google_auth_oauthlib-0.4.1-py2.py3-none-any.whl (18 kB)
    Collecting cachetools<5.0,>=2.0.0
      Using cached cachetools-4.1.1-py3-none-any.whl (10 kB)
    Requirement already satisfied, skipping upgrade: rsa<5,>=3.1.4; python_version >= "3" in /usr/local/lib/python3.6/dist-packages (from google-auth<2,>=1.6.3->tensorboard<2.3.0,>=2.2.0->tensorflow>=2.1.0->tensortrade[baselines,ccxt,fbm,ta,tensorforce,tf]) (3.4.2)
    Collecting pyasn1-modules>=0.2.1
      Using cached pyasn1_modules-0.2.8-py2.py3-none-any.whl (155 kB)
    Requirement already satisfied, skipping upgrade: chardet<3.1.0,>=3.0.2 in /usr/local/lib/python3.6/dist-packages (from requests<3,>=2.21.0->tensorboard<2.3.0,>=2.2.0->tensorflow>=2.1.0->tensortrade[baselines,ccxt,fbm,ta,tensorforce,tf]) (3.0.4)
    Requirement already satisfied, skipping upgrade: urllib3!=1.25.0,!=1.25.1,<1.26,>=1.21.1 in /usr/local/lib/python3.6/dist-packages (from requests<3,>=2.21.0->tensorboard<2.3.0,>=2.2.0->tensorflow>=2.1.0->tensortrade[baselines,ccxt,fbm,ta,tensorforce,tf]) (1.25.8)
    Requirement already satisfied, skipping upgrade: idna<2.9,>=2.5 in /usr/local/lib/python3.6/dist-packages (from requests<3,>=2.21.0->tensorboard<2.3.0,>=2.2.0->tensorflow>=2.1.0->tensortrade[baselines,ccxt,fbm,ta,tensorforce,tf]) (2.8)
    Requirement already satisfied, skipping upgrade: certifi>=2017.4.17 in /usr/local/lib/python3.6/dist-packages (from requests<3,>=2.21.0->tensorboard<2.3.0,>=2.2.0->tensorflow>=2.1.0->tensortrade[baselines,ccxt,fbm,ta,tensorforce,tf]) (2019.11.28)
    Collecting requests-oauthlib>=0.7.0
      Using cached requests_oauthlib-1.3.0-py2.py3-none-any.whl (23 kB)
    Requirement already satisfied, skipping upgrade: pyasn1>=0.1.3 in /usr/local/lib/python3.6/dist-packages (from rsa<5,>=3.1.4; python_version >= "3"->google-auth<2,>=1.6.3->tensorboard<2.3.0,>=2.2.0->tensorflow>=2.1.0->tensortrade[baselines,ccxt,fbm,ta,tensorforce,tf]) (0.4.8)
    Collecting oauthlib>=3.0.0
      Using cached oauthlib-3.1.0-py2.py3-none-any.whl (147 kB)
    Building wheels for collected packages: tensortrade
      Building wheel for tensortrade (setup.py) ... done
      Created wheel for tensortrade: filename=tensortrade-0.2.0b2-py3-none-any.whl size=120451 sha256=a0e4ed3804a1a0ac07b6d9b02ecb42a6ddcc7e1bbedaa7a7815f881d0fbdfa58
      Stored in directory: /tmp/pip-ephem-wheel-cache-aarnqgx6/wheels/7d/90/2a/bb771201465fd1314ad06c9290fdcc23ca3389e8b75ef54a32
    Successfully built tensortrade
    ERROR: tensorflow-gpu 1.15.2 has requirement gast==0.2.2, but you'll have gast 0.3.3 which is incompatible.
    ERROR: tensorflow-gpu 1.15.2 has requirement tensorboard<1.16.0,>=1.15.0, but you'll have tensorboard 2.2.2 which is incompatible.
    ERROR: tensorflow-gpu 1.15.2 has requirement tensorflow-estimator==1.15.1, but you'll have tensorflow-estimator 2.2.0 which is incompatible.
    ERROR: tensorflow 2.2.0 has requirement scipy==1.4.1; python_version >= "3", but you'll have scipy 1.2.2 which is incompatible.
    Installing collected packages: tensorflow-estimator, astunparse, gast, cachetools, pyasn1-modules, google-auth, tensorboard-plugin-wit, oauthlib, requests-oauthlib, google-auth-oauthlib, tensorboard, tensorflow, tensortrade
      Attempting uninstall: tensorflow-estimator
        Found existing installation: tensorflow-estimator 1.15.1
        Uninstalling tensorflow-estimator-1.15.1:
          Successfully uninstalled tensorflow-estimator-1.15.1
      Attempting uninstall: gast
        Found existing installation: gast 0.2.2
        Uninstalling gast-0.2.2:
          Successfully uninstalled gast-0.2.2
      Attempting uninstall: tensorboard
        Found existing installation: tensorboard 1.15.0
        Uninstalling tensorboard-1.15.0:
          Successfully uninstalled tensorboard-1.15.0
      Attempting uninstall: tensortrade
        Found existing installation: tensortrade 0.2.0b2
        Uninstalling tensortrade-0.2.0b2:
          Successfully uninstalled tensortrade-0.2.0b2
    Successfully installed astunparse-1.6.3 cachetools-4.1.1 gast-0.3.3 google-auth-1.18.0 google-auth-oauthlib-0.4.1 oauthlib-3.1.0 pyasn1-modules-0.2.8 requests-oauthlib-1.3.0 tensorboard-2.2.2 tensorboard-plugin-wit-1.7.0 tensorflow-2.2.0 tensorflow-estimator-2.2.0 tensortrade-0.2.0b2
    Processing ./.cache/pip/wheels/6f/ad/f4/4a269deab015672fd1ab353d6b2c3fcf64f413980737c13541/yfinance-0.1.54-py2.py3-none-any.whl
    Processing ./.cache/pip/wheels/29/57/ae/6f5b674713533f27be1da94af552aa867186a81b67639c96b1/ta-0.5.25-py3-none-any.whl
    Collecting matplotlib
      Using cached matplotlib-3.2.2-cp36-cp36m-manylinux1_x86_64.whl (12.4 MB)
    Collecting s3fs
      Using cached s3fs-0.4.2-py3-none-any.whl (19 kB)
    Requirement already satisfied: pandas>=0.24 in /usr/local/lib/python3.6/dist-packages (from yfinance) (1.0.5)
    Requirement already satisfied: requests>=2.20 in /usr/local/lib/python3.6/dist-packages (from yfinance) (2.22.0)
    Processing ./.cache/pip/wheels/5e/8a/c4/59c699498647c7c94b14c87a904ca7540646107b3d94b7c320/multitasking-0.0.9-py3-none-any.whl
    Requirement already satisfied: numpy>=1.15 in /usr/local/lib/python3.6/dist-packages (from yfinance) (1.18.2)
    Collecting kiwisolver>=1.0.1
      Using cached kiwisolver-1.2.0-cp36-cp36m-manylinux1_x86_64.whl (88 kB)
    Collecting cycler>=0.10
      Using cached cycler-0.10.0-py2.py3-none-any.whl (6.5 kB)
    Requirement already satisfied: python-dateutil>=2.1 in /usr/local/lib/python3.6/dist-packages (from matplotlib) (2.8.1)
    Requirement already satisfied: pyparsing!=2.0.4,!=2.1.2,!=2.1.6,>=2.0.1 in /usr/local/lib/python3.6/dist-packages (from matplotlib) (2.4.6)
    Requirement already satisfied: botocore>=1.12.91 in /usr/local/lib/python3.6/dist-packages (from s3fs) (1.15.24)
    Collecting fsspec>=0.6.0
      Using cached fsspec-0.7.4-py3-none-any.whl (75 kB)
    Requirement already satisfied: pytz>=2017.2 in /usr/local/lib/python3.6/dist-packages (from pandas>=0.24->yfinance) (2019.3)
    Requirement already satisfied: chardet<3.1.0,>=3.0.2 in /usr/local/lib/python3.6/dist-packages (from requests>=2.20->yfinance) (3.0.4)
    Requirement already satisfied: certifi>=2017.4.17 in /usr/local/lib/python3.6/dist-packages (from requests>=2.20->yfinance) (2019.11.28)
    Requirement already satisfied: urllib3!=1.25.0,!=1.25.1,<1.26,>=1.21.1 in /usr/local/lib/python3.6/dist-packages (from requests>=2.20->yfinance) (1.25.8)
    Requirement already satisfied: idna<2.9,>=2.5 in /usr/local/lib/python3.6/dist-packages (from requests>=2.20->yfinance) (2.8)
    Requirement already satisfied: six in /usr/local/lib/python3.6/dist-packages (from cycler>=0.10->matplotlib) (1.14.0)
    Requirement already satisfied: jmespath<1.0.0,>=0.7.1 in /usr/local/lib/python3.6/dist-packages (from botocore>=1.12.91->s3fs) (0.9.5)
    Requirement already satisfied: docutils<0.16,>=0.10 in /usr/local/lib/python3.6/dist-packages (from botocore>=1.12.91->s3fs) (0.15.2)
    Installing collected packages: multitasking, yfinance, ta, kiwisolver, cycler, matplotlib, fsspec, s3fs
    Successfully installed cycler-0.10.0 fsspec-0.7.4 kiwisolver-1.2.0 matplotlib-3.2.2 multitasking-0.0.9 s3fs-0.4.2 ta-0.5.25 yfinance-0.1.54

### Declare variables that are used in this analysis

```python
symbol = 'SPY'
exchange = 'NASDAQ'
start_date = '2010-01-01'
end_date = '2020-06-30'
```

### Fetch EOD data of ETF/stock
Option 1: Fetch data from Yahoo Finance

```python
import yfinance as yf
data = yf.download(symbol, start=start_date, end=end_date)
data.head()
```

    [*********************100%***********************]  1 of 1 completed

      Open
      High
      Low
      Close
      Adj Close
      Volume

      Date

      2010-01-04
      112.370003
      113.389999
      111.510002
      113.330002
      91.841896
      118944600

      2010-01-05
      113.260002
      113.680000
      112.849998
      113.629997
      92.084984
      111579900

      2010-01-06
      113.519997
      113.989998
      113.430000
      113.709999
      92.149803
      116074400

      2010-01-07
      113.500000
      114.330002
      113.180000
      114.190002
      92.538841
      131091100

      2010-01-08
      113.889999
      114.620003
      113.660004
      114.570000
      92.846756
      126402800

(Optional) Save the ETF/stock EOD data in S3

```python
import sagemaker
bucket = sagemaker.Session().default_bucket()
prefix = 'data/'
data.reset_index(inplace=True)
data.to_csv('s3://'+ bucket + '/' + prefix + symbol + '.csv', index=False)
```

Option 2: Fetch data from S3 bucket

```python
#import io
import pandas as pd
import sagemaker
bucket = sagemaker.Session().default_bucket()
prefix = 'data/'
data = pd.read_csv('s3://' + bucket + '/' + prefix + symbol + '.csv')
data.set_index('Date', inplace=True)
data.head()
```

      Open
      High
      Low
      Close
      Adj Close
      Volume

      Date

      2010-01-04
      112.370003
      113.389999
      111.510002
      113.330002
      91.841896
      118944600

      2010-01-05
      113.260002
      113.680000
      112.849998
      113.629997
      92.084984
      111579900

      2010-01-06
      113.519997
      113.989998
      113.430000
      113.709999
      92.149803
      116074400

      2010-01-07
      113.500000
      114.330002
      113.180000
      114.190002
      92.538841
      131091100

      2010-01-08
      113.889999
      114.620003
      113.660004
      114.570000
      92.846756
      126402800

### Generate features based on [technical analysis](https://github.com/bukosabino/ta) of the ETF/stock

```python
import ta
ta.add_all_ta_features(data, 'Open', 'High', 'Low', 'Close', 'Volume', fillna=True)
data.columns = [symbol + ":" + name.lower() for name in data.columns]
data.head()
```

    /usr/local/lib/python3.6/dist-packages/ta/trend.py:608: RuntimeWarning: invalid value encountered in double_scalars
      dip[i] = 100 * (self._dip[i]/self._trs[i])
    /usr/local/lib/python3.6/dist-packages/ta/trend.py:612: RuntimeWarning: invalid value encountered in double_scalars
      din[i] = 100 * (self._din[i]/self._trs[i])

      SPY:open
      SPY:high
      SPY:low
      SPY:close
      SPY:adj close
      SPY:volume
      SPY:volume_adi
      SPY:volume_obv
      SPY:volume_cmf
      SPY:volume_fi
      ...
      SPY:momentum_uo
      SPY:momentum_stoch
      SPY:momentum_stoch_signal
      SPY:momentum_wr
      SPY:momentum_ao
      SPY:momentum_kama
      SPY:momentum_roc
      SPY:others_dr
      SPY:others_dlr
      SPY:others_cr

      Date

      2010-01-04
      112.370003
      113.389999
      111.510002
      113.330002
      91.841896
      118944600
      1.113527e+08
      118944600
      0.936173
      0.000000e+00
      ...
      0.000000
      96.808636
      96.808636
      -3.191364
      0.0
      113.330002
      0.0
      -43.637407
      0.000000
      0.000000

      2010-01-05
      113.260002
      113.680000
      112.849998
      113.629997
      92.084984
      111579900
      2.094884e+08
      230524500
      0.908747
      3.347346e+07
      ...
      28.782253
      97.695710
      97.252173
      -2.304290
      0.0
      113.471767
      0.0
      0.264710
      0.264360
      0.264710

      2010-01-06
      113.519997
      113.989998
      113.430000
      113.709999
      92.149803
      116074400
      2.094884e+08
      346598900
      0.604412
      3.001813e+07
      ...
      32.415861
      88.709707
      94.404684
      -11.290293
      0.0
      113.584301
      0.0
      0.070406
      0.070381
      0.335302

      2010-01-07
      113.500000
      114.330002
      113.180000
      114.190002
      92.538841
      131091100
      3.086619e+08
      477690000
      0.646155
      3.471899e+07
      ...
      46.832592
      95.035482
      93.813633
      -4.964518
      0.0
      113.859769
      0.0
      0.422129
      0.421241
      0.758846

      2010-01-08
      113.889999
      114.620003
      113.660004
      114.570000
      92.846756
      126402800
      4.218969e+08
      604092800
      0.698398
      3.662095e+07
      ...
      55.390283
      98.392185
      94.045791
      -1.607815
      0.0
      114.186429
      0.0
      0.332776
      0.332224
      1.094148

5 rows × 78 columns

### Create a 'DataFeed' for the Gym TradingEnvironment to be able to observe and make actions about
- Make a list of Stream objects, one for each DataFrame column

```python
from tensortrade.data import Module, Stream

with Module(exchange) as node_stream:
    nodes = []
    for name in data.columns:
        nodes.append(Stream(list(data[name]), name))
```

```python
from tensortrade.data import DataFeed
data_feed = DataFeed([node_stream])
```

```python
data_feed.next() # Show 1 slice of the data
```

    {'NASDAQ:/SPY:open': 112.37000274658205,
     'NASDAQ:/SPY:high': 113.38999938964844,
     'NASDAQ:/SPY:low': 111.51000213623048,
     'NASDAQ:/SPY:close': 113.33000183105469,
     'NASDAQ:/SPY:adj close': 91.8418960571289,
     'NASDAQ:/SPY:volume': 118944600,
     'NASDAQ:/SPY:volume_adi': 111352689.32573092,
     'NASDAQ:/SPY:volume_obv': 118944600,
     'NASDAQ:/SPY:volume_cmf': 0.9361727167583137,
     'NASDAQ:/SPY:volume_fi': 0.0,
     'NASDAQ:/SPY:momentum_mfi': 50.0,
     'NASDAQ:/SPY:volume_em': 0.0,
     'NASDAQ:/SPY:volume_sma_em': 0.0,
     'NASDAQ:/SPY:volume_vpt': -52024532.18648957,
     'NASDAQ:/SPY:volume_nvi': 1000.0,
     'NASDAQ:/SPY:volume_vwap': 112.7433344523112,
     'NASDAQ:/SPY:volatility_atr': 0.0,
     'NASDAQ:/SPY:volatility_bbm': 113.33000183105469,
     'NASDAQ:/SPY:volatility_bbh': 113.33000183105469,
     'NASDAQ:/SPY:volatility_bbl': 113.33000183105469,
     'NASDAQ:/SPY:volatility_bbw': 0.0,
     'NASDAQ:/SPY:volatility_bbp': 0.0,
     'NASDAQ:/SPY:volatility_bbhi': 0.0,
     'NASDAQ:/SPY:volatility_bbli': 0.0,
     'NASDAQ:/SPY:volatility_kcc': 112.7433344523112,
     'NASDAQ:/SPY:volatility_kch': 114.62333170572917,
     'NASDAQ:/SPY:volatility_kcl': 110.86333719889325,
     'NASDAQ:/SPY:volatility_kcw': 3.335003816501761,
     'NASDAQ:/SPY:volatility_kcp': 0.6560287861263832,
     'NASDAQ:/SPY:volatility_kchi': 0.0,
     'NASDAQ:/SPY:volatility_kcli': 0.0,
     'NASDAQ:/SPY:volatility_dcl': 1.6588698694459896,
     'NASDAQ:/SPY:volatility_dch': 0.9680863583791569,
     'NASDAQ:/SPY:trend_macd': 0.0,
     'NASDAQ:/SPY:trend_macd_signal': 0.0,
     'NASDAQ:/SPY:trend_macd_diff': 0.0,
     'NASDAQ:/SPY:trend_sma_fast': 113.33000183105469,
     'NASDAQ:/SPY:trend_sma_slow': 113.33000183105469,
     'NASDAQ:/SPY:trend_ema_fast': 113.33000183105469,
     'NASDAQ:/SPY:trend_ema_slow': 113.33000183105469,
     'NASDAQ:/SPY:trend_adx': 0.0,
     'NASDAQ:/SPY:trend_adx_pos': 0.0,
     'NASDAQ:/SPY:trend_adx_neg': 0.0,
     'NASDAQ:/SPY:trend_vortex_ind_pos': 0.0,
     'NASDAQ:/SPY:trend_vortex_ind_neg': 0.0,
     'NASDAQ:/SPY:trend_vortex_ind_diff': 0.0,
     'NASDAQ:/SPY:trend_trix': -43.20567852926306,
     'NASDAQ:/SPY:trend_mass_index': 1.0,
     'NASDAQ:/SPY:trend_cci': 0.0,
     'NASDAQ:/SPY:trend_dpo': 87.74307761047825,
     'NASDAQ:/SPY:trend_kst': -436.3740678472662,
     'NASDAQ:/SPY:trend_kst_sig': -436.3740678472662,
     'NASDAQ:/SPY:trend_kst_diff': 0.0,
     'NASDAQ:/SPY:trend_ichimoku_conv': 112.45000076293945,
     'NASDAQ:/SPY:trend_ichimoku_base': 112.45000076293945,
     'NASDAQ:/SPY:trend_ichimoku_a': 112.45000076293945,
     'NASDAQ:/SPY:trend_ichimoku_b': 112.45000076293945,
     'NASDAQ:/SPY:trend_visual_ichimoku_a': 199.68840442570772,
     'NASDAQ:/SPY:trend_visual_ichimoku_b': 197.45571224183746,
     'NASDAQ:/SPY:trend_aroon_up': 4.0,
     'NASDAQ:/SPY:trend_aroon_down': 4.0,
     'NASDAQ:/SPY:trend_aroon_ind': 0.0,
     'NASDAQ:/SPY:trend_psar_up': -1.0,
     'NASDAQ:/SPY:trend_psar_down': -1.0,
     'NASDAQ:/SPY:trend_psar_up_indicator': 0.0,
     'NASDAQ:/SPY:trend_psar_down_indicator': 0.0,
     'NASDAQ:/SPY:momentum_rsi': 100.0,
     'NASDAQ:/SPY:momentum_tsi': 0.0,
     'NASDAQ:/SPY:momentum_uo': 0.0,
     'NASDAQ:/SPY:momentum_stoch': 96.80863583791569,
     'NASDAQ:/SPY:momentum_stoch_signal': 96.80863583791569,
     'NASDAQ:/SPY:momentum_wr': -3.1913641620843127,
     'NASDAQ:/SPY:momentum_ao': 0.0,
     'NASDAQ:/SPY:momentum_kama': 113.33000183105469,
     'NASDAQ:/SPY:momentum_roc': 0.0,
     'NASDAQ:/SPY:others_dr': -43.63740678472663,
     'NASDAQ:/SPY:others_dlr': 0.0,
     'NASDAQ:/SPY:others_cr': 0.0}

### Define Exchange
An exchange needs a name, an execution service, and streams of price data in order to function properly.

The setups supported right now are the simulated execution service using simulated or stochastic data.

More execution services will be made available in the future, as well as price streams so that live data and execution can be supported.

```python
from tensortrade.exchanges import Exchange
from tensortrade.exchanges.services.execution.simulated import execute_order

# Make a stream of closing prices to make orders on
exchange = Exchange("sim-exchange", service=execute_order)(Stream(list(data['SPY:close']), "USD-SPY"))
```

### Portfolio
- Pass in as many Wallet objects as assets you are working with
- This example uses SPY as an instrument, the complete list of instruments are available [here](https://github.com/msrparadesi/tensortrade/blob/master/tensortrade/instruments/instrument.py)

```python
from tensortrade.instruments import USD, SPY
from tensortrade.wallets import Portfolio, Wallet

portfolio = Portfolio(
    base_instrument = USD,
    wallets = [Wallet(exchange, 100000 * USD),
               Wallet(exchange, 0 * SPY)]
)
```

### Gym Environment
- We pass in the datafeed + portfolio objects
- And set as many other parameters as desired

```python
from tensortrade.environments import TradingEnvironment

env = TradingEnvironment(
    feed=data_feed,
    portfolio=portfolio,
    action_scheme='managed-risk',
    reward_scheme='risk-adjusted',
    window_size=20
)
```

#### Example DataFeed Observation at any given step in the data

```python
env.feed.next() # Show 1 slice of the feed's data
```

    {'NASDAQ:/SPY:open': 112.37000274658205,
     'NASDAQ:/SPY:high': 113.38999938964844,
     'NASDAQ:/SPY:low': 111.51000213623048,
     'NASDAQ:/SPY:close': 113.33000183105469,
     'NASDAQ:/SPY:adj close': 91.8418960571289,
     'NASDAQ:/SPY:volume': 118944600,
     'NASDAQ:/SPY:volume_adi': 111352689.32573092,
     'NASDAQ:/SPY:volume_obv': 118944600,
     'NASDAQ:/SPY:volume_cmf': 0.9361727167583137,
     'NASDAQ:/SPY:volume_fi': 0.0,
     'NASDAQ:/SPY:momentum_mfi': 50.0,
     'NASDAQ:/SPY:volume_em': 0.0,
     'NASDAQ:/SPY:volume_sma_em': 0.0,
     'NASDAQ:/SPY:volume_vpt': -52024532.18648957,
     'NASDAQ:/SPY:volume_nvi': 1000.0,
     'NASDAQ:/SPY:volume_vwap': 112.7433344523112,
     'NASDAQ:/SPY:volatility_atr': 0.0,
     'NASDAQ:/SPY:volatility_bbm': 113.33000183105469,
     'NASDAQ:/SPY:volatility_bbh': 113.33000183105469,
     'NASDAQ:/SPY:volatility_bbl': 113.33000183105469,
     'NASDAQ:/SPY:volatility_bbw': 0.0,
     'NASDAQ:/SPY:volatility_bbp': 0.0,
     'NASDAQ:/SPY:volatility_bbhi': 0.0,
     'NASDAQ:/SPY:volatility_bbli': 0.0,
     'NASDAQ:/SPY:volatility_kcc': 112.7433344523112,
     'NASDAQ:/SPY:volatility_kch': 114.62333170572917,
     'NASDAQ:/SPY:volatility_kcl': 110.86333719889325,
     'NASDAQ:/SPY:volatility_kcw': 3.335003816501761,
     'NASDAQ:/SPY:volatility_kcp': 0.6560287861263832,
     'NASDAQ:/SPY:volatility_kchi': 0.0,
     'NASDAQ:/SPY:volatility_kcli': 0.0,
     'NASDAQ:/SPY:volatility_dcl': 1.6588698694459896,
     'NASDAQ:/SPY:volatility_dch': 0.9680863583791569,
     'NASDAQ:/SPY:trend_macd': 0.0,
     'NASDAQ:/SPY:trend_macd_signal': 0.0,
     'NASDAQ:/SPY:trend_macd_diff': 0.0,
     'NASDAQ:/SPY:trend_sma_fast': 113.33000183105469,
     'NASDAQ:/SPY:trend_sma_slow': 113.33000183105469,
     'NASDAQ:/SPY:trend_ema_fast': 113.33000183105469,
     'NASDAQ:/SPY:trend_ema_slow': 113.33000183105469,
     'NASDAQ:/SPY:trend_adx': 0.0,
     'NASDAQ:/SPY:trend_adx_pos': 0.0,
     'NASDAQ:/SPY:trend_adx_neg': 0.0,
     'NASDAQ:/SPY:trend_vortex_ind_pos': 0.0,
     'NASDAQ:/SPY:trend_vortex_ind_neg': 0.0,
     'NASDAQ:/SPY:trend_vortex_ind_diff': 0.0,
     'NASDAQ:/SPY:trend_trix': -43.20567852926306,
     'NASDAQ:/SPY:trend_mass_index': 1.0,
     'NASDAQ:/SPY:trend_cci': 0.0,
     'NASDAQ:/SPY:trend_dpo': 87.74307761047825,
     'NASDAQ:/SPY:trend_kst': -436.3740678472662,
     'NASDAQ:/SPY:trend_kst_sig': -436.3740678472662,
     'NASDAQ:/SPY:trend_kst_diff': 0.0,
     'NASDAQ:/SPY:trend_ichimoku_conv': 112.45000076293945,
     'NASDAQ:/SPY:trend_ichimoku_base': 112.45000076293945,
     'NASDAQ:/SPY:trend_ichimoku_a': 112.45000076293945,
     'NASDAQ:/SPY:trend_ichimoku_b': 112.45000076293945,
     'NASDAQ:/SPY:trend_visual_ichimoku_a': 199.68840442570772,
     'NASDAQ:/SPY:trend_visual_ichimoku_b': 197.45571224183746,
     'NASDAQ:/SPY:trend_aroon_up': 4.0,
     'NASDAQ:/SPY:trend_aroon_down': 4.0,
     'NASDAQ:/SPY:trend_aroon_ind': 0.0,
     'NASDAQ:/SPY:trend_psar_up': -1.0,
     'NASDAQ:/SPY:trend_psar_down': -1.0,
     'NASDAQ:/SPY:trend_psar_up_indicator': 0.0,
     'NASDAQ:/SPY:trend_psar_down_indicator': 0.0,
     'NASDAQ:/SPY:momentum_rsi': 100.0,
     'NASDAQ:/SPY:momentum_tsi': 0.0,
     'NASDAQ:/SPY:momentum_uo': 0.0,
     'NASDAQ:/SPY:momentum_stoch': 96.80863583791569,
     'NASDAQ:/SPY:momentum_stoch_signal': 96.80863583791569,
     'NASDAQ:/SPY:momentum_wr': -3.1913641620843127,
     'NASDAQ:/SPY:momentum_ao': 0.0,
     'NASDAQ:/SPY:momentum_kama': 113.33000183105469,
     'NASDAQ:/SPY:momentum_roc': 0.0,
     'NASDAQ:/SPY:others_dr': -43.63740678472663,
     'NASDAQ:/SPY:others_dlr': 0.0,
     'NASDAQ:/SPY:others_cr': 0.0,
     'sim-exchange:/USD-SPY': 113.33000183105469,
     'sim-exchange:/USD:/free': 100000.0,
     'sim-exchange:/USD:/locked': 0.0,
     'sim-exchange:/USD:/total': 100000.0,
     'sim-exchange:/SPY:/free': 0.0,
     'sim-exchange:/SPY:/locked': 0.0,
     'sim-exchange:/SPY:/total': 0.0,
     'sim-exchange:/SPY:/worth': 0.0,
     'net_worth': 100000.0}

## Setup and Train DQN Agent
- Passing in the environment on initialization
- Then running the DQNAgent's train() function

```python
from tensortrade.agents import DQNAgent

agent = DQNAgent(env)

!mkdir -p agents/

reward = agent.train(n_steps=100, save_path="agents/", n_episodes = 100)
```

    ====      AGENT ID: 29e7a11a-ce40-40b7-afe0-20149566e7d9      ====
    [2020-07-05 14:26:24 PM] Episode: 2/100 Step: 50/100
    [2020-07-05 14:26:33 PM] Episode: 2/100 Step: 100/100
    [2020-07-05 14:26:43 PM] Episode: 3/100 Step: 50/100
    [2020-07-05 14:26:52 PM] Episode: 3/100 Step: 100/100
    [2020-07-05 14:27:02 PM] Episode: 4/100 Step: 50/100
    [2020-07-05 14:27:11 PM] Episode: 4/100 Step: 100/100
    [2020-07-05 14:27:21 PM] Episode: 5/100 Step: 50/100
    [2020-07-05 14:27:31 PM] Episode: 5/100 Step: 100/100
    [2020-07-05 14:27:40 PM] Episode: 6/100 Step: 50/100
    [2020-07-05 14:27:50 PM] Episode: 6/100 Step: 100/100
    [2020-07-05 14:27:59 PM] Episode: 7/100 Step: 50/100
    [2020-07-05 14:28:09 PM] Episode: 7/100 Step: 100/100
    [2020-07-05 14:28:19 PM] Episode: 8/100 Step: 50/100
    [2020-07-05 14:28:28 PM] Episode: 8/100 Step: 100/100
    [2020-07-05 14:28:37 PM] Episode: 9/100 Step: 50/100
    [2020-07-05 14:28:47 PM] Episode: 9/100 Step: 100/100
    [2020-07-05 14:28:56 PM] Episode: 10/100 Step: 50/100
    [2020-07-05 14:29:06 PM] Episode: 10/100 Step: 100/100
    [2020-07-05 14:29:15 PM] Episode: 11/100 Step: 50/100
    [2020-07-05 14:29:25 PM] Episode: 11/100 Step: 100/100
    [2020-07-05 14:29:34 PM] Episode: 12/100 Step: 50/100
    [2020-07-05 14:29:44 PM] Episode: 12/100 Step: 100/100
    [2020-07-05 14:29:53 PM] Episode: 13/100 Step: 50/100
    [2020-07-05 14:30:03 PM] Episode: 13/100 Step: 100/100
    [2020-07-05 14:30:13 PM] Episode: 14/100 Step: 50/100
    [2020-07-05 14:30:22 PM] Episode: 14/100 Step: 100/100
    [2020-07-05 14:30:32 PM] Episode: 15/100 Step: 50/100
    [2020-07-05 14:30:41 PM] Episode: 15/100 Step: 100/100
    [2020-07-05 14:30:51 PM] Episode: 16/100 Step: 50/100
    [2020-07-05 14:31:00 PM] Episode: 16/100 Step: 100/100
    [2020-07-05 14:31:10 PM] Episode: 17/100 Step: 50/100
    [2020-07-05 14:31:19 PM] Episode: 17/100 Step: 100/100
    [2020-07-05 14:31:29 PM] Episode: 18/100 Step: 50/100
    [2020-07-05 14:31:38 PM] Episode: 18/100 Step: 100/100
    [2020-07-05 14:31:48 PM] Episode: 19/100 Step: 50/100
    [2020-07-05 14:31:57 PM] Episode: 19/100 Step: 100/100
    [2020-07-05 14:32:07 PM] Episode: 20/100 Step: 50/100
    [2020-07-05 14:32:16 PM] Episode: 20/100 Step: 100/100
    [2020-07-05 14:32:26 PM] Episode: 21/100 Step: 50/100
    [2020-07-05 14:32:35 PM] Episode: 21/100 Step: 100/100
    [2020-07-05 14:32:45 PM] Episode: 22/100 Step: 50/100
    [2020-07-05 14:32:54 PM] Episode: 22/100 Step: 100/100
    [2020-07-05 14:33:03 PM] Episode: 23/100 Step: 50/100
    [2020-07-05 14:33:13 PM] Episode: 23/100 Step: 100/100
    [2020-07-05 14:33:22 PM] Episode: 24/100 Step: 50/100
    [2020-07-05 14:33:32 PM] Episode: 24/100 Step: 100/100
    [2020-07-05 14:33:41 PM] Episode: 25/100 Step: 50/100
    [2020-07-05 14:33:50 PM] Episode: 25/100 Step: 100/100
    [2020-07-05 14:34:00 PM] Episode: 26/100 Step: 50/100
    [2020-07-05 14:34:09 PM] Episode: 26/100 Step: 100/100
    [2020-07-05 14:34:19 PM] Episode: 27/100 Step: 50/100
    [2020-07-05 14:34:28 PM] Episode: 27/100 Step: 100/100
    [2020-07-05 14:34:38 PM] Episode: 28/100 Step: 50/100
    [2020-07-05 14:34:47 PM] Episode: 28/100 Step: 100/100
    [2020-07-05 14:34:57 PM] Episode: 29/100 Step: 50/100
    [2020-07-05 14:35:06 PM] Episode: 29/100 Step: 100/100
    [2020-07-05 14:35:16 PM] Episode: 30/100 Step: 50/100
    [2020-07-05 14:35:25 PM] Episode: 30/100 Step: 100/100
    [2020-07-05 14:35:35 PM] Episode: 31/100 Step: 50/100
    [2020-07-05 14:35:44 PM] Episode: 31/100 Step: 100/100
    [2020-07-05 14:35:54 PM] Episode: 32/100 Step: 50/100
    [2020-07-05 14:36:03 PM] Episode: 32/100 Step: 100/100
    [2020-07-05 14:36:13 PM] Episode: 33/100 Step: 50/100
    [2020-07-05 14:36:22 PM] Episode: 33/100 Step: 100/100
    [2020-07-05 14:36:32 PM] Episode: 34/100 Step: 50/100
    [2020-07-05 14:36:41 PM] Episode: 34/100 Step: 100/100
    [2020-07-05 14:36:51 PM] Episode: 35/100 Step: 50/100
    [2020-07-05 14:37:00 PM] Episode: 35/100 Step: 100/100
    [2020-07-05 14:37:10 PM] Episode: 36/100 Step: 50/100
    [2020-07-05 14:37:19 PM] Episode: 36/100 Step: 100/100
    [2020-07-05 14:37:29 PM] Episode: 37/100 Step: 50/100
    [2020-07-05 14:37:38 PM] Episode: 37/100 Step: 100/100
    [2020-07-05 14:37:48 PM] Episode: 38/100 Step: 50/100
    [2020-07-05 14:37:57 PM] Episode: 38/100 Step: 100/100
    [2020-07-05 14:38:07 PM] Episode: 39/100 Step: 50/100
    [2020-07-05 14:38:16 PM] Episode: 39/100 Step: 100/100
    [2020-07-05 14:38:26 PM] Episode: 40/100 Step: 50/100
    [2020-07-05 14:38:35 PM] Episode: 40/100 Step: 100/100
    [2020-07-05 14:38:45 PM] Episode: 41/100 Step: 50/100
    [2020-07-05 14:38:54 PM] Episode: 41/100 Step: 100/100
    [2020-07-05 14:39:04 PM] Episode: 42/100 Step: 50/100
    [2020-07-05 14:39:13 PM] Episode: 42/100 Step: 100/100
    [2020-07-05 14:39:23 PM] Episode: 43/100 Step: 50/100
    [2020-07-05 14:39:32 PM] Episode: 43/100 Step: 100/100
    [2020-07-05 14:39:41 PM] Episode: 44/100 Step: 50/100
    [2020-07-05 14:39:51 PM] Episode: 44/100 Step: 100/100
    [2020-07-05 14:40:00 PM] Episode: 45/100 Step: 50/100
    [2020-07-05 14:40:10 PM] Episode: 45/100 Step: 100/100
    [2020-07-05 14:40:19 PM] Episode: 46/100 Step: 50/100
    [2020-07-05 14:40:29 PM] Episode: 46/100 Step: 100/100
    [2020-07-05 14:40:38 PM] Episode: 47/100 Step: 50/100
    [2020-07-05 14:40:48 PM] Episode: 47/100 Step: 100/100
    [2020-07-05 14:40:57 PM] Episode: 48/100 Step: 50/100
    [2020-07-05 14:41:07 PM] Episode: 48/100 Step: 100/100
    [2020-07-05 14:41:16 PM] Episode: 49/100 Step: 50/100
    [2020-07-05 14:41:26 PM] Episode: 49/100 Step: 100/100
    [2020-07-05 14:41:35 PM] Episode: 50/100 Step: 50/100
    [2020-07-05 14:41:45 PM] Episode: 50/100 Step: 100/100
    [2020-07-05 14:41:54 PM] Episode: 51/100 Step: 50/100
    [2020-07-05 14:42:04 PM] Episode: 51/100 Step: 100/100
    [2020-07-05 14:42:13 PM] Episode: 52/100 Step: 50/100
    [2020-07-05 14:42:23 PM] Episode: 52/100 Step: 100/100
    [2020-07-05 14:42:32 PM] Episode: 53/100 Step: 50/100
    [2020-07-05 14:42:42 PM] Episode: 53/100 Step: 100/100
    [2020-07-05 14:42:51 PM] Episode: 54/100 Step: 50/100
    [2020-07-05 14:43:01 PM] Episode: 54/100 Step: 100/100
    [2020-07-05 14:43:11 PM] Episode: 55/100 Step: 50/100
    [2020-07-05 14:43:20 PM] Episode: 55/100 Step: 100/100
    [2020-07-05 14:43:30 PM] Episode: 56/100 Step: 50/100
    [2020-07-05 14:43:39 PM] Episode: 56/100 Step: 100/100
    [2020-07-05 14:43:48 PM] Episode: 57/100 Step: 50/100
    [2020-07-05 14:43:58 PM] Episode: 57/100 Step: 100/100
    [2020-07-05 14:44:07 PM] Episode: 58/100 Step: 50/100
    [2020-07-05 14:44:17 PM] Episode: 58/100 Step: 100/100
    [2020-07-05 14:44:26 PM] Episode: 59/100 Step: 50/100
    [2020-07-05 14:44:36 PM] Episode: 59/100 Step: 100/100
    [2020-07-05 14:44:45 PM] Episode: 60/100 Step: 50/100
    [2020-07-05 14:44:54 PM] Episode: 60/100 Step: 100/100
    [2020-07-05 14:45:04 PM] Episode: 61/100 Step: 50/100
    [2020-07-05 14:45:14 PM] Episode: 61/100 Step: 100/100
    [2020-07-05 14:45:23 PM] Episode: 62/100 Step: 50/100
    [2020-07-05 14:45:33 PM] Episode: 62/100 Step: 100/100
    [2020-07-05 14:45:42 PM] Episode: 63/100 Step: 50/100
    [2020-07-05 14:45:51 PM] Episode: 63/100 Step: 100/100
    [2020-07-05 14:46:01 PM] Episode: 64/100 Step: 50/100
    [2020-07-05 14:46:10 PM] Episode: 64/100 Step: 100/100
    [2020-07-05 14:46:20 PM] Episode: 65/100 Step: 50/100
    [2020-07-05 14:46:29 PM] Episode: 65/100 Step: 100/100
    [2020-07-05 14:46:38 PM] Episode: 66/100 Step: 50/100
    [2020-07-05 14:46:48 PM] Episode: 66/100 Step: 100/100
    [2020-07-05 14:46:57 PM] Episode: 67/100 Step: 50/100
    [2020-07-05 14:47:07 PM] Episode: 67/100 Step: 100/100
    [2020-07-05 14:47:16 PM] Episode: 68/100 Step: 50/100
    [2020-07-05 14:47:25 PM] Episode: 68/100 Step: 100/100
    [2020-07-05 14:47:35 PM] Episode: 69/100 Step: 50/100
    [2020-07-05 14:47:44 PM] Episode: 69/100 Step: 100/100
    [2020-07-05 14:47:54 PM] Episode: 70/100 Step: 50/100
    [2020-07-05 14:48:03 PM] Episode: 70/100 Step: 100/100
    [2020-07-05 14:48:13 PM] Episode: 71/100 Step: 50/100
    [2020-07-05 14:48:22 PM] Episode: 71/100 Step: 100/100
    [2020-07-05 14:48:32 PM] Episode: 72/100 Step: 50/100
    [2020-07-05 14:48:41 PM] Episode: 72/100 Step: 100/100
    [2020-07-05 14:48:50 PM] Episode: 73/100 Step: 50/100
    [2020-07-05 14:49:00 PM] Episode: 73/100 Step: 100/100
    [2020-07-05 14:49:09 PM] Episode: 74/100 Step: 50/100
    [2020-07-05 14:49:18 PM] Episode: 74/100 Step: 100/100
    [2020-07-05 14:49:28 PM] Episode: 75/100 Step: 50/100
    [2020-07-05 14:49:37 PM] Episode: 75/100 Step: 100/100
    [2020-07-05 14:49:47 PM] Episode: 76/100 Step: 50/100
    [2020-07-05 14:49:56 PM] Episode: 76/100 Step: 100/100
    [2020-07-05 14:50:06 PM] Episode: 77/100 Step: 50/100
    [2020-07-05 14:50:15 PM] Episode: 77/100 Step: 100/100
    [2020-07-05 14:50:25 PM] Episode: 78/100 Step: 50/100
    [2020-07-05 14:50:34 PM] Episode: 78/100 Step: 100/100
    [2020-07-05 14:50:44 PM] Episode: 79/100 Step: 50/100
    [2020-07-05 14:50:53 PM] Episode: 79/100 Step: 100/100
    [2020-07-05 14:51:03 PM] Episode: 80/100 Step: 50/100
    [2020-07-05 14:51:12 PM] Episode: 80/100 Step: 100/100
    [2020-07-05 14:51:22 PM] Episode: 81/100 Step: 50/100
    [2020-07-05 14:51:31 PM] Episode: 81/100 Step: 100/100
    [2020-07-05 14:51:40 PM] Episode: 82/100 Step: 50/100
    [2020-07-05 14:51:50 PM] Episode: 82/100 Step: 100/100
    [2020-07-05 14:51:59 PM] Episode: 83/100 Step: 50/100
    [2020-07-05 14:52:09 PM] Episode: 83/100 Step: 100/100
    [2020-07-05 14:52:18 PM] Episode: 84/100 Step: 50/100
    [2020-07-05 14:52:28 PM] Episode: 84/100 Step: 100/100
    [2020-07-05 14:52:37 PM] Episode: 85/100 Step: 50/100
    [2020-07-05 14:52:46 PM] Episode: 85/100 Step: 100/100
    [2020-07-05 14:52:56 PM] Episode: 86/100 Step: 50/100
    [2020-07-05 14:53:05 PM] Episode: 86/100 Step: 100/100
    [2020-07-05 14:53:15 PM] Episode: 87/100 Step: 50/100
    [2020-07-05 14:53:24 PM] Episode: 87/100 Step: 100/100
    [2020-07-05 14:53:34 PM] Episode: 88/100 Step: 50/100
    [2020-07-05 14:53:43 PM] Episode: 88/100 Step: 100/100
    [2020-07-05 14:53:53 PM] Episode: 89/100 Step: 50/100
    [2020-07-05 14:54:02 PM] Episode: 89/100 Step: 100/100
    [2020-07-05 14:54:12 PM] Episode: 90/100 Step: 50/100
    [2020-07-05 14:54:21 PM] Episode: 90/100 Step: 100/100
    [2020-07-05 14:54:30 PM] Episode: 91/100 Step: 50/100
    [2020-07-05 14:54:40 PM] Episode: 91/100 Step: 100/100
    [2020-07-05 14:54:49 PM] Episode: 92/100 Step: 50/100
    [2020-07-05 14:54:59 PM] Episode: 92/100 Step: 100/100
    [2020-07-05 14:55:08 PM] Episode: 93/100 Step: 50/100
    [2020-07-05 14:55:18 PM] Episode: 93/100 Step: 100/100
    [2020-07-05 14:55:27 PM] Episode: 94/100 Step: 50/100
    [2020-07-05 14:55:37 PM] Episode: 94/100 Step: 100/100
    [2020-07-05 14:55:46 PM] Episode: 95/100 Step: 50/100
    [2020-07-05 14:55:55 PM] Episode: 95/100 Step: 100/100
    [2020-07-05 14:56:05 PM] Episode: 96/100 Step: 50/100
    [2020-07-05 14:56:14 PM] Episode: 96/100 Step: 100/100
    [2020-07-05 14:56:24 PM] Episode: 97/100 Step: 50/100
    [2020-07-05 14:56:33 PM] Episode: 97/100 Step: 100/100
    [2020-07-05 14:56:43 PM] Episode: 98/100 Step: 50/100
    [2020-07-05 14:56:52 PM] Episode: 98/100 Step: 100/100
    [2020-07-05 14:57:02 PM] Episode: 99/100 Step: 50/100
    [2020-07-05 14:57:11 PM] Episode: 99/100 Step: 100/100
    [2020-07-05 14:57:21 PM] Episode: 100/100 Step: 50/100
    [2020-07-05 14:57:30 PM] Episode: 100/100 Step: 100/100

## Plot Performance
- Prints out charts after training

```python
portfolio.performance.plot()
```

```python
portfolio.performance.net_worth.plot()
```