Merge pull request #363 from DidierRLopes/dark-pools

Add dark pools (ATS) vs Non-ATS data over time
This commit is contained in:
DidierRLopes
2021-04-18 21:36:41 +01:00
committed by GitHub
5 changed files with 330 additions and 0 deletions

View File

@@ -79,6 +79,7 @@
---
## Due Diligence
* [x] Add dark pools (ATS) vs Non-ATS data over time (@didier) - [PR #363](https://github.com/DidierRLopes/GamestonkTerminal/pull/363)
**NEXT**

View File

@@ -213,6 +213,7 @@ Command|Description|Source
`sec` |SEC filings |[MarketWatch](https://www.marketwatch.com/)
`short` |short interest |[Quandl](https://www.quandl.com/)
`warnings` |company warnings according to Sean Seah book |[MarketWatch](https://www.marketwatch.com/)
`dp` |dark pools (ATS) vs OTC data [FINRA](https://www.finra.org/#/)
 

View File

@@ -24,6 +24,8 @@ This menu aims to help in due-diligence of a pre-loaded stock, and the usage of
* short interest [Quandl]
* [warnings](#warnings)
* company warnings according to Sean Seah book [Market Watch]
* [dp](#dp)
* dark pools (ATS) vs OTC data [FINRA]
## news <a name="news"></a>
@@ -157,3 +159,15 @@ Sean Seah warnings. Check: Consistent historical earnings per share; Consistentl
* -d : print insights into warnings calculation. Default False.
<img width="927" alt="warnings" src="https://user-images.githubusercontent.com/25267873/108609497-2ec03780-73c6-11eb-8577-d5da80dae213.png">
## dp <a name="dp"></a>
```text
usage: dp
```
Display barchart of dark pool (ATS) and OTC (Non ATS) data
![dp](https://user-images.githubusercontent.com/25267873/115130908-7987b580-9feb-11eb-8bca-1999174178d0.png)

View File

@@ -13,6 +13,7 @@ from gamestonk_terminal.due_diligence import market_watch_view as mw_view
from gamestonk_terminal.due_diligence import quandl_view as q_view
from gamestonk_terminal.due_diligence import reddit_view as r_view
from gamestonk_terminal.due_diligence import news_view
from gamestonk_terminal.due_diligence import finra_view
from gamestonk_terminal import feature_flags as gtff
from gamestonk_terminal.helper_funcs import get_flair
from gamestonk_terminal.menu import session
@@ -37,6 +38,7 @@ class DueDiligenceController:
"analyst",
"warnings",
"sec",
"dp",
]
def __init__(self, stock: DataFrame, ticker: str, start: str, interval: str):
@@ -97,6 +99,7 @@ class DueDiligenceController:
print(
" warnings company warnings according to Sean Seah book [Market Watch]"
)
print(" dp dark pools (ATS) vs OTC data [FINRA]")
print("")
def switch(self, an_input: str):
@@ -175,6 +178,10 @@ class DueDiligenceController:
""" Process short command """
q_view.short_interest(other_args, self.ticker, self.start)
def call_dp(self, other_args: List[str]):
""" Process dp command """
finra_view.dark_pool(other_args, self.ticker)
def menu(stock: DataFrame, ticker: str, start: str, interval: str):
"""Due Diligence Menu

View File

@@ -0,0 +1,307 @@
""" FINRA View """
__docformat__ = "numpy"
import argparse
from typing import List, Tuple
import requests
import pandas as pd
from matplotlib import pyplot as plt
from gamestonk_terminal.config_plot import PLOT_DPI
from gamestonk_terminal import feature_flags as gtff
from gamestonk_terminal.helper_funcs import parse_known_args_and_warn, plot_autoscale
def getFINRAweeks(tier, is_ats) -> List:
"""Get FINRA weeks
Parameters
----------
tier : str
Stock tier between T1, T2, or OTCE
is_ats : bool
ATS data if true, NON-ATS otherwise
Returns
----------
List
List of response data
"""
req_hdr = {"Accept": "application/json", "Content-Type": "application/json"}
req_data = {
"compareFilters": [
{
"compareType": "EQUAL",
"fieldName": "summaryTypeCode",
"fieldValue": "ATS_W_SMBL" if is_ats else "OTC_W_SMBL",
},
{
"compareType": "EQUAL",
"fieldName": "tierIdentifier",
"fieldValue": tier,
},
],
"delimiter": "|",
"fields": ["weekStartDate"],
"limit": 27,
"quoteValues": False,
"sortFields": ["-weekStartDate"],
}
response = requests.post(
"https://api.finra.org/data/group/otcMarket/name/weeklyDownloadDetails",
headers=req_hdr,
json=req_data,
)
return response.json() if response.status_code == 200 else list()
def getFINRAdata(weekStartDate, tier, ticker, is_ats) -> Tuple[int, List]:
"""Get FINRA data
Parameters
----------
weekStartDate : str
Weekly data to get FINRA data
tier : str
Stock tier between T1, T2, or OTCE
ticker : str
Stock ticker to get data from
is_ats : bool
ATS data if true, NON-ATS otherwise
Returns
----------
str
Status code from request
List
List of response data
"""
req_hdr = {"Accept": "application/json", "Content-Type": "application/json"}
l_cmp_filters = [
{
"compareType": "EQUAL",
"fieldName": "weekStartDate",
"fieldValue": weekStartDate,
},
{"compareType": "EQUAL", "fieldName": "tierIdentifier", "fieldValue": tier},
{
"compareType": "EQUAL",
"description": "",
"fieldName": "summaryTypeCode",
"fieldValue": "ATS_W_SMBL" if is_ats else "OTC_W_SMBL",
},
]
if ticker:
l_cmp_filters.append(
{
"compareType": "EQUAL",
"fieldName": "issueSymbolIdentifier",
"fieldValue": ticker,
}
)
req_data = {
"compareFilters": l_cmp_filters,
"delimiter": "|",
"fields": [
"issueSymbolIdentifier",
"totalWeeklyShareQuantity",
"totalWeeklyTradeCount",
"lastUpdateDate",
],
"limit": 5000,
"quoteValues": False,
"sortFields": ["totalWeeklyShareQuantity"],
}
response = requests.post(
"https://api.finra.org/data/group/otcMarket/name/weeklySummary",
headers=req_hdr,
json=req_data,
)
return (
response.status_code,
response.json() if response.status_code == 200 else list(),
)
def getTickerFINRAdata(ticker) -> Tuple[pd.DataFrame, pd.DataFrame]:
"""Get all FINRA data associated with a ticker
Parameters
----------
ticker : str
Stock ticker to get data from
Returns
----------
pd.DataFrame
Dark Pools (ATS) Data
pd.DataFrame
OTC (Non-ATS) Data
"""
tiers = ["T1", "T2", "OTCE"]
l_data = list()
for tier in tiers:
for d_week in getFINRAweeks(tier, is_ats=True):
status_code, response = getFINRAdata(
d_week["weekStartDate"], tier, ticker, True
)
if status_code == 200:
if response:
d_data = response[0]
d_data.update(d_week)
l_data.append(d_data)
else:
break
df_ats = pd.DataFrame(l_data)
if not df_ats.empty:
df_ats = df_ats.sort_values("weekStartDate")
df_ats = df_ats.set_index("weekStartDate")
l_data = list()
for tier in tiers:
for d_week in getFINRAweeks(tier, is_ats=False):
status_code, response = getFINRAdata(
d_week["weekStartDate"], tier, ticker, False
)
if status_code == 200:
if response:
d_data = response[0]
d_data.update(d_week)
l_data.append(d_data)
else:
break
df_otc = pd.DataFrame(l_data)
if not df_otc.empty:
df_otc = df_otc.sort_values("weekStartDate")
df_otc = df_otc.set_index("weekStartDate")
return df_ats, df_otc
def plot_dark_pools(ticker: str, ats: pd.DataFrame, otc: pd.DataFrame):
"""Plots ATS and NON-ATS data
Parameters
----------
ticker : str
Stock ticker to get data from
ats : pd.DataFrame
Dark Pools (ATS) Data
otc : pd.DataFrame
OTC (Non-ATS) Data
"""
_, axData = plt.subplots(figsize=plot_autoscale(), dpi=PLOT_DPI)
plt.subplot(3, 1, (1, 2))
if not ats.empty and not otc.empty:
plt.bar(
ats.index,
(ats["totalWeeklyShareQuantity"] + otc["totalWeeklyShareQuantity"])
/ 1_000_000,
color="tab:orange",
)
plt.bar(
otc.index, otc["totalWeeklyShareQuantity"] / 1_000_000, color="tab:blue"
)
plt.legend(["ATS", "OTC"])
elif not ats.empty:
plt.bar(
ats.index,
ats["totalWeeklyShareQuantity"] / 1_000_000,
color="tab:orange",
)
plt.legend(["ATS"])
elif not otc.empty:
plt.bar(
otc.index, otc["totalWeeklyShareQuantity"] / 1_000_000, color="tab:blue"
)
plt.legend(["OTC"])
plt.ylabel("Total Weekly Shares [Million]")
plt.grid(b=True, which="major", color="#666666", linestyle="-", alpha=0.2)
plt.title(f"Dark Pools (ATS) vs OTC (Non-ATS) Data for {ticker}")
plt.subplot(313)
if not ats.empty:
plt.plot(
ats.index,
ats["totalWeeklyShareQuantity"] / ats["totalWeeklyTradeCount"],
color="tab:orange",
)
plt.legend(["ATS"])
if not otc.empty:
plt.plot(
otc.index,
otc["totalWeeklyShareQuantity"] / otc["totalWeeklyTradeCount"],
color="tab:blue",
)
plt.legend(["ATS", "OTC"])
else:
plt.plot(
otc.index,
otc["totalWeeklyShareQuantity"] / otc["totalWeeklyTradeCount"],
color="tab:blue",
)
plt.legend(["OTC"])
plt.ylabel("Shares per Trade")
plt.grid(b=True, which="major", color="#666666", linestyle="-", alpha=0.2)
plt.gcf().autofmt_xdate()
plt.xlabel("Weeks")
if gtff.USE_ION:
plt.ion()
plt.show()
def dark_pool(other_args: List[str], ticker: str):
"""Display barchart of dark pool (ATS) and OTC (Non ATS) data
Parameters
----------
other_args : List[str]
argparse other args
ticker : str
Stock ticker
"""
parser = argparse.ArgumentParser(
add_help=False,
prog="dp",
description="Display barchart of dark pool (ATS) and OTC (Non ATS) data [Source: FINRA]",
)
if 1:
ns_parser = parse_known_args_and_warn(parser, other_args)
if not ns_parser:
return
df_ats, df_otc = getTickerFINRAdata(ticker)
if df_ats.empty and df_otc.empty:
print("No ticker data found!")
plot_dark_pools(ticker, df_ats, df_otc)
print("")
"""
except Exception as e:
print(e)
print("")
return
"""