Skip to content

Commit

Permalink
Merge pull request #16 from sharmasourab93/indices
Browse files Browse the repository at this point in the history
NSE Indices
  • Loading branch information
sharmasourab93 authored Jun 2, 2024
2 parents 391b0f3 + a812892 commit 54682d6
Show file tree
Hide file tree
Showing 35 changed files with 1,230 additions and 105 deletions.
5 changes: 3 additions & 2 deletions .github/workflows/ci-format.yml
Original file line number Diff line number Diff line change
@@ -1,9 +1,10 @@
name: Lint with Black & Format with isort and Push
name: Run Format & Lint


on:
push:
branches:
- '*'
- "*"

jobs:
format:
Expand Down
File renamed without changes.
17 changes: 16 additions & 1 deletion configs/nse.json
Original file line number Diff line number Diff line change
Expand Up @@ -57,7 +57,7 @@
"DERIVATIVE-MASTER-LIST": "api/master-quote",
"DERIVATIVE-OPTION-CHAIN": "api/option-chain-equities?symbol={0}",
"DERIVATIVE-OPTION-INDEX": "api/option-chain-indices?symbol={0}",
"DERIVATIVE-QUOTE": "api/quote-derivative?symbol={0}"
"DERIVATIVE-INDEX-CHOICE": ["NIFTYNXT50", "FINNIFTY", "MIDCPNIFTY", "BANKNIFTY", "NIFTY"]
},
"EQUITY": {
"EQUITY-META": "api/equity-meta-info?symbol={0}",
Expand All @@ -74,6 +74,21 @@
"TRADING": "api/holiday-master?type=trading"
},
"INDEX": {
"NSE-GENERAL-SYMBOLS": ["NIFTY 50", "NIFTY BANK", "NIFTY FINANCIAL SERVICES", "NIFTY NEXT 50", "NIFTY MIDCAP 50"],
"YFIN-NSE-SYMBOLS": {
"NIFTY 50": "^NSEI",
"NIFTY BANK": "^NSEBANK",
"NIFTY FINANCIAL SERVICES": "^NIFTY_FIN_SERVICE.NS",
"NIFTY NEXT 50": "^NSMIDCP",
"NIFTY MIDCAP 50": "NSEMDCP50"
},
"NSE-API-SYMBOLS": {
"NIFTY 50": "NIFTY",
"NIFTY NEXT 50": "NIFTYNXT50",
"NIFTY FINANCIAL SERVICES": "FINNIFTY",
"NIFTY MIDCAP 50": "MIDCPNIFTY",
"NIFTY BANK": "BANKNIFTY"
},
"EQUITY-MASTER": "api/equity-master",
"INDEX-NAMES": "api/index-names",
"INDICES": "api/allIndices",
Expand Down
3 changes: 2 additions & 1 deletion setup.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
from setuptools import find_packages, setup

NAME = "market-generic"
__version__ = "0.1.0"
__version__ = "0.2.0"
DESCRITPION = "A Generic Python Package for Stock Market Analysis and Trading"
URL = "https://github.com/sharmasourab93/market-generic"
AUTHOR = "Sourab S Sharma"
Expand All @@ -25,6 +25,7 @@
long_description_content_type="text/markdown",
url=URL,
packages=find_packages(exclude=["tests", ".github"]),
package_data={"market-generic": ["configs/nse.json"]},
setup_requires=["wheel"],
install_requires=INSTALL_REQUIRES,
keywords=KEYWORDS,
Expand Down
61 changes: 61 additions & 0 deletions tests/test_nse/test_indices/test_nse_indices_config.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
from datetime import datetime

import pandas as pd
import pytest

from trade.nse.indices.nse_indices_config import INDICES, NSEIndexConfig
from trade.nse.nse_config import DATE_FMT

MARKET, COUNTRY = "NSE", "INDIA"
DATED = datetime.today().strftime(DATE_FMT)


@pytest.fixture
def index_config():
return NSEIndexConfig(DATED, market=MARKET, country=COUNTRY)


def test_get_fii_dii(index_config):
fii_dii = index_config.get_fii_dii_report()
assert isinstance(fii_dii, list)
assert len(fii_dii) > 0
assert all(isinstance(item, dict) for item in fii_dii)


def test_get_all_indices(index_config):
indices = index_config.get_all_indices()
assert isinstance(indices, dict)
assert len(indices) > 0
assert all(
isinstance(key, str) and isinstance(value, pd.DataFrame)
for key, value in indices.items()
)


def test_get_quote_index(index_config):
for index in INDICES:
quote = index_config.get_quote_index(index)
assert isinstance(quote, dict)
cols = ("adv-dec", "symbol", "ohlc", "dated", "status")
assert all(i in cols for i in quote.keys())
assert quote["symbol"] == index


def test_get_quote_index_invalid_input(index_config):
with pytest.raises(AttributeError):
index_config.get_quote_index(None)


def test_get_index_metrics(index_config):

response = index_config.get_index_metrics()

assert isinstance(response, dict)
assert all(i in INDICES for i in response.keys())


def test_get_vix_history(index_config):
start, end = "01-05-2024", "01-06-2024"
response = index_config.get_vix_history(start, end)

assert isinstance(response, dict)
39 changes: 39 additions & 0 deletions tests/test_nse/test_nse_config.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
from datetime import date, datetime
from pathlib import Path

import pytest
import requests
Expand All @@ -15,6 +16,13 @@ def nse_config():
return NSEConfig(DATED, market=MARKET, country=COUNTRY)


@pytest.fixture
def nse_config_with_log_config():
log_path = Path(__file__).parent.parent.parent
log_config = dict(date_fmt=DATE_FMT, log_path=log_path, level=10)
return NSEConfig(DATED, market=MARKET, country=COUNTRY, log_config=log_config)


def test_advanced_header(nse_config):
assert isinstance(nse_config.advanced_header, dict)

Expand Down Expand Up @@ -75,3 +83,34 @@ def mock_download_data(url, headers):

with pytest.raises(requests.exceptions.RequestException):
nse_config.get_eq_bhavcopy()


def test_if_logger_exists(nse_config):
does_logger_exists = hasattr(nse_config, "logger")
assert not does_logger_exists


def test_if_logger_exists_after_config(nse_config_with_log_config):
does_logger_exists = hasattr(nse_config_with_log_config, "logger")
log_file_path = Path(__file__).parent.parent.parent / Path("log")
assert does_logger_exists
assert log_file_path.exists()


def test_get_all_etfs(nse_config):

response = nse_config.get_all_etfs()

assert isinstance(response, dict)
assert isinstance(response["data"], list)
assert all(isinstance(i, dict) for i in response["data"])
assert len(response["data"]) > 100


@pytest.mark.parametrize(
"symbol", ["NIFTY", "NIFTYNXT50", "BANKNIFTY", "RELIANCE", "SBIN"]
)
def test_get_option_chain(nse_config, symbol):
response = nse_config.get_option_chain_data(symbol)
assert isinstance(response, dict)
assert all(i in response.keys() for i in ("records", "filtered"))
Original file line number Diff line number Diff line change
@@ -1,8 +1,6 @@
from unittest.mock import MagicMock

import pytest

from trade.nse.nse_all_stocks import NSE_TOP, AllNSEStocks
from trade.nse.stocks.nse_all_stocks import AllNSEStocks


def test_all_nse_stocks_post_init_with_symbols():
Expand Down
Original file line number Diff line number Diff line change
@@ -1,9 +1,8 @@
from unittest.mock import MagicMock

import pandas as pd
import pytest

from trade.nse.nse_stock import NSEStock
from trade.nse.stocks.nse_stock import NSEStock


@pytest.fixture
Expand Down
41 changes: 41 additions & 0 deletions tests/test_option_chain/test_index_option_chain.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
import pytest

from trade.nse.indices.nse_indices_config import NSEIndexConfig
from trade.option_chain.index_option_chain import IndexOptionChainAnalysis


@pytest.fixture
def index_option_chain_analysis():
return IndexOptionChainAnalysis(
symbol="NIFTY",
oc_data={
"records": {
"data": [
{"strikePrice": 100, "CE_openInterest": 100, "PE_openInterest": 100}
]
},
"filtered": {"PE": {"totOI": 100}, "CE": {"totOI": 100}},
"timestamp": "2022-01-01 00:00:00",
"expiryDates": ["2022-01-15", "2022-02-15"],
"strikePrices": [100, 110, 120],
"underlyingValue": 100,
},
strike_multiples={"NIFTY": 10},
Config=NSEIndexConfig,
)


@pytest.mark.xfail(reason="Still not well integrated.")
def test_index_option_chain_analysis(index_option_chain_analysis):
result = index_option_chain_analysis.index_option_chain_analysis(
symbol="NIFTY", expiry_day=15, delta=5, expiry_delta=1
)
assert isinstance(result, dict)
assert "symbol" in result
assert "Expiry" in result
assert "Max Put & Call OI" in result
assert "Support at" in result
assert "Resistance at" in result
assert "Straddle" in result
assert "Overall PCR" in result
assert "Verdict" in result
51 changes: 51 additions & 0 deletions tests/test_option_chain/test_stock_option_chain.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
# import pytest
#
# from trade.nse.nse_config import NSEConfig
# from trade.option_chain.stock_option_chain import StockOptionChain
#
#
# @pytest.fixture
# def stock_option_chain():
# return StockOptionChain(
# symbol="TATASTEEL",
# oc_data={
# "records": {
# "data": [
# {"strikePrice": 100, "CE_openInterest": 100, "PE_openInterest": 100}
# ]
# },
# "filtered": {"PE": {"totOI": 100}, "CE": {"totOI": 100}},
# "timestamp": "2022-01-01 00:00:00",
# "expiryDates": ["2022-01-15", "2022-02-15"],
# "strikePrices": [100, 110, 120],
# "underlyingValue": 100,
# },
# strike_multiples={"TATASTEEL": 10},
# Config=NSEConfig,
# )
#
#
# pytest.mark.xfail(reason="Not Integrated yet.")
# def test_get_select_stock_options_bhavcopy(stock_option_chain):
# result = stock_option_chain.get_select_stock_options_bhavcopy(
# symbol="TATASTEEL", expiry="2022-01-15"
# )
# assert isinstance(result, tuple)
# assert len(result) == 2
# assert isinstance(result[0], pd.DataFrame)
# assert isinstance(result[1], pd.DataFrame)
# assert result[0].shape[0] > 0
# assert result[1].shape[0] > 0
#
#
# pytest.mark.xfail(reason="Not Integrated.")
#
#
# def test_stock_option_chain(stock_option_chain):
# result = stock_option_chain.stock_option_chain(
# symbol="TATASTEEL", delta=5, expiry_delta=1
# )
# assert isinstance(result, dict)
# assert "OptionChain" in result
# assert "Expiry" in result
# assert "underlying_value" in result
2 changes: 1 addition & 1 deletion tests/test_ticker/test_api_config.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

import pytest

from trade.ticker.api_config import APIConfig
from trade.exchange.api_config import APIConfig


@pytest.fixture(scope="function")
Expand Down
2 changes: 1 addition & 1 deletion tests/test_ticker/test_exchange_data.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
from pandas import DataFrame

from trade.calendar import MarketCalendar, MarketHolidayType
from trade.ticker import Exchange
from trade.exchange import Exchange
from trade.utils import LoggingType

FROZEN_DATE = "2024-04-29 05:10"
Expand Down
2 changes: 1 addition & 1 deletion tests/test_ticker/test_yf.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import pytest

from trade.ticker.yf import YFinance
from trade.exchange.yf import YFinance


@pytest.fixture
Expand Down
1 change: 1 addition & 0 deletions trade/exchange/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
from trade.exchange.market import Exchange, ExchangeArgs
File renamed without changes.
4 changes: 2 additions & 2 deletions trade/ticker/market.py → trade/exchange/market.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,8 @@
from typing import Callable, Dict, List, Optional, Union

from trade.calendar import MarketCalendar, MarketHolidayType, MarketTimingType
from trade.ticker.api_config import APIConfig
from trade.ticker.yf import YFinance
from trade.exchange.api_config import APIConfig
from trade.exchange.yf import YFinance
from trade.utils import DownloadTools, Logger, LoggingType


Expand Down
File renamed without changes.
36 changes: 36 additions & 0 deletions trade/nse/data_generics.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
from typing import Literal, Optional, TypeVar

from trade.option_chain.index_option_chain import IndexOptionChainAnalysis
from trade.option_chain.stock_option_chain import StockOptionChain


class NSEDataGeneric:

def get_option_chain_analysis(
self, oc_type: Literal["index", "equity"]
) -> "OptionChainType":

match oc_type:

case "index":
return self.option_chain_result(IndexOptionChainAnalysis)

case "equity":
return (
self.option_chain_result(StockOptionChain) if self.is_fno else None
)

case _:
raise KeyError()

def option_chain_result(self, OptionChain: type):

option_chain_data = (
self._nse_config.get_option_chain_data.get_option_chain_data(self.symbol)
)

oc_obj = OptionChain(symbol, option_chain_data, self.dated, strike_multiples)

return oc_obj.option_chain_output()

def apply_indicators(self, Indicator: type) -> None: ...
Empty file added trade/nse/indices/__init__.py
Empty file.
Loading

0 comments on commit 54682d6

Please sign in to comment.