Skip to content

Commit

Permalink
Merge pull request #14 from jk1mm/dev
Browse files Browse the repository at this point in the history
Merge dev to release
  • Loading branch information
jk1mm authored Jan 15, 2021
2 parents ee47742 + ce0208e commit ee174ae
Show file tree
Hide file tree
Showing 7 changed files with 192 additions and 6 deletions.
7 changes: 7 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,14 @@ work in the making.
1) General success metrics on recent IPO bubble
2) Optimal sell day analysis
3) Individual stock performance views


- **Index**: Analysis on a market index
1) Stock categorization summary by industry
2) Index performance for different periodic times
3) Today's top and bottom performing stocks


- **Stocks**: Stock net profit calculator
1) Supposed net gain on a stock for a buy and sell on a specified
date period
25 changes: 25 additions & 0 deletions docs/analysis/stocks.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
## Stock Analysis


The **stock_profit** function within the analysis directory contains net profit
view for a particular stock of interest. The following features are shown in the
code snippet below.

```python
# Python

# Import module
from stock_market.analysis.stocks import stock_profit

# Get the supposed net profit if 100 AAPL stocks were purchased
# at market open on January 7 2021 and sold one week after before market close
print(
stock_profit(ticker="AAPL",
quantity=100,
purchase_date="2021-01-07",
sell_date="2021-01-14",
purchase_time="Open",
sell_time="Close")
)

```
2 changes: 1 addition & 1 deletion requirements/requirements.txt
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
pandas >= 1.1.5
numpy >= 1.19.4
pandas-datareader >= 0.9.0
pandas-datareader == 0.9.0
bs4 >= 0.0.1
requests >= 2.25.1
plotly >= 4.14.1
4 changes: 2 additions & 2 deletions setup.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,15 +3,15 @@

setup(
name="stock_market",
version="1.1.2",
version="1.1.3",
description="Modules related to stock market model.",
author="Josh Kim",
author_email="joshkim47@gmail.com",
packages=find_packages(exclude=["docs", "tests*"]),
install_requires=[
"pandas>=1.1.5",
"numpy>=1.19.4",
"pandas-datareader>=0.9.0",
"pandas-datareader==0.9.0",
"bs4>=0.0.1",
"requests>=2.25.1",
"plotly>=4.14.1",
Expand Down
99 changes: 99 additions & 0 deletions stock_market/analysis/stocks.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,99 @@
from stock_market.data import get_ticker
from typing import Optional
import pandas as pd
import warnings


MARKET_TIME = [
"open", # at market open
"close", # at market close
"high", # at day high price
"low", # at day low price
]


def stock_profit(
ticker: str,
quantity: int,
purchase_date: str,
sell_date: str = None,
purchase_time: str = "open",
sell_time: str = "close",
) -> Optional[float]:
"""
Stock calculator, to understand the net profit from buying and selling n number of stocks on a
certain period of time.
Parameters
----------
ticker: str
Stock ticker symbol.
quantity: int
Number of stocks purchased/sold.
purchase_date: str
Date of stock purchase.
sell_date: str, default None
Date of stock sell. If None, use current date.
purchase_time: str, default "open"
Time of purchase.
sell_time: str, default "close"
Time of sell.
Returns
-------
net_profit: Optional[float]
Net profit, in the respective exchange currency.
"""
# Check if purchase or sell time is valid
purchase_time = purchase_time.lower()
sell_time = sell_time.lower()
if purchase_time not in MARKET_TIME or sell_time not in MARKET_TIME:
raise Exception(f"Populate valid time metrics: {MARKET_TIME}")

# TODO: Think of a more efficient way to do this

# Call the ticker data
ticker_history = get_ticker(
ticker=ticker, start_date=purchase_date, end_date=sell_date, new_metrics=False
)

# If no data is returned (from invalid date range)
if ticker_history is None:
print("No stock history for requested date range.")
return None

# Checking validity of the requested days

# For buy date, shift to next nearest day if invalid
if pd.to_datetime(purchase_date) != ticker_history.index[0]:
warnings.warn("Purchase date has been shifted to the next stock in market day.")

# For sell date, shift to previous nearest day
if pd.to_datetime(sell_date) != ticker_history.index[-1]:
warnings.warn(
"Sell date has been shifted back to the previous stock in market day."
)

# Re-format column names for standardization
ticker_history.columns = ["high", "low", "open", "close", "volume", "adj close"]

# Calculation

# Edge case: for length == 1, raise warning for potential inaccurate results
if len(ticker_history) == 1:
warnings.warn(
"Result may be inaccurate since buying and selling happens on the same day."
)

# Calculate
net_profit = (ticker_history.loc[:, sell_time][-1] * quantity) - (
ticker_history.loc[:, purchase_time][0] * quantity
)

return net_profit
10 changes: 7 additions & 3 deletions stock_market/data/_stocks.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ def get_ticker(
ticker: str,
start_date: str,
end_date: str = None,
new_metrics: bool = True,
) -> Optional[pd.DataFrame]:
"""
Extracts stock prices over a date period from Yahoo Finance.
Expand All @@ -26,6 +27,9 @@ def get_ticker(
end_date: str, default None
End date of stock information. If None, use current date.
new_metrics: bool, default True
Option to get additional metrics.
Returns
-------
stock_data: Optional[pd.DataFrame]
Expand All @@ -43,10 +47,10 @@ def get_ticker(
try:
stock_data = data.DataReader(ticker, "yahoo", start_date, end_date)
except KeyError:
stock_data = None
return None

# Adding new metrics
if stock_data is not None:
# Adding new metrics if requested
if new_metrics:

# 1) Day to day percent change
if len(stock_data) >= 2:
Expand Down
51 changes: 51 additions & 0 deletions tests/test_analysis_stocks.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
import pytest

from stock_market.analysis.stocks import stock_profit


def test_stock_profit():
# TODO: Add edge case of potential start and end date convergence

# Invalid market time raises exception
with pytest.raises(Exception):
stock_profit(
ticker="AAPL",
quantity=100,
purchase_date="2020-12-10",
purchase_time="Invalid time",
)

# Invalid date range
assert (
stock_profit(
ticker="AAPL",
quantity=100,
purchase_date="2100-01-01",
sell_date="2100-01-08",
)
is None
)

# Raise warning for non market date specification
with pytest.warns(UserWarning):
stock_profit(
ticker="AAPL",
quantity=100,
purchase_date="2021-01-02",
sell_date="2021-01-09",
)

# Raise warning for one day ticker history
with pytest.warns(UserWarning):
stock_profit(
ticker="AAPL",
quantity=100,
purchase_date="2021-01-07",
sell_date="2021-01-07",
)

# Check Value calculation results
assert (
stock_profit(ticker="aapl", quantity=1, purchase_date="2021-01-14")
== -1.8899993896484375
)

0 comments on commit ee174ae

Please sign in to comment.