From 0e12b97177fd002ef11b81458a12ecf827f45e67 Mon Sep 17 00:00:00 2001 From: Tarun Wadhwa Date: Mon, 5 Oct 2020 01:38:33 +0530 Subject: [PATCH 001/335] Place order should return its response so user can find exchange_order_id, etc --- py5paisa/py5paisa.py | 1 + 1 file changed, 1 insertion(+) diff --git a/py5paisa/py5paisa.py b/py5paisa/py5paisa.py index 32102c4e..be952a8b 100644 --- a/py5paisa/py5paisa.py +++ b/py5paisa/py5paisa.py @@ -122,6 +122,7 @@ def order_request(self, req_type) -> None: res = self.session.post(url, json=self.payload, headers=HEADERS).json() log_response(res["body"]["Message"]) + return res["body"] def fetch_order_status(self, req_list: RequestList) -> dict: self.payload["body"]["OrdStatusReqList"] = req_list.orders From b38902140daad0ba9443801135c636eee9c47404 Mon Sep 17 00:00:00 2001 From: Rahul Tiwari Date: Sun, 3 Jan 2021 23:33:45 +0530 Subject: [PATCH 002/335] V0.1.7 (#35) * feat: login V3 * fix workflow * bump to 0.1.7 --- .github/workflows/pythonpublish.yml | 2 +- docs/usage/place.md | 6 +++--- py5paisa/py5paisa.py | 4 ++-- setup.py | 2 +- 4 files changed, 7 insertions(+), 7 deletions(-) diff --git a/.github/workflows/pythonpublish.yml b/.github/workflows/pythonpublish.yml index 9f7ce26d..5fdb9e42 100644 --- a/.github/workflows/pythonpublish.yml +++ b/.github/workflows/pythonpublish.yml @@ -15,7 +15,7 @@ jobs: - name: Set up Python uses: actions/setup-python@v1 with: - python-version: '3.8.5' + python-version: '3.8.6' - name: Install dependencies run: | python -m pip install --upgrade pip diff --git a/docs/usage/place.md b/docs/usage/place.md index 230477e5..81e6e81b 100644 --- a/docs/usage/place.md +++ b/docs/usage/place.md @@ -5,7 +5,7 @@ from py5paisa.order import Order, OrderType -test_order = Order(order_type=OrderType.BUY, scrip_code=11111, quantity=10) +test_order = Order(order_type=OrderType.BUY, scrip_code=11111, quantity=10, price=123.0) client.place_order(test_order) @@ -20,13 +20,13 @@ By default orders are delivery, to place an intraday order, pass `is_intraday=Tr ```py from py5paisa.order import Order, OrderType -test_order = Order(order_type=OrderType.BUY, scrip_code=11111, quantity=10, is_intraday=True) +test_order = Order(order_type=OrderType.BUY, scrip_code=11111, quantity=10, is_intraday=True, price=123.0) client.place_order(test_order) ``` #### Placing a stoploss order -By default orders are regular, to place a stoploss order, pass `is_stoploss_order=True` and `stoploss_price` +By default orders are regular, to place a stoploss order, pass `is_stoploss_order=True` and `stoploss_price` which is the trigger price for your order. `stoploss_price` is a float and for Buy Stop loss, Trigger price should not be greater than Limit Price. And for Sell Stop Loss Order Trigger Price should not be less than Limit Price. diff --git a/py5paisa/py5paisa.py b/py5paisa/py5paisa.py index be952a8b..4ce1f932 100644 --- a/py5paisa/py5paisa.py +++ b/py5paisa/py5paisa.py @@ -10,7 +10,7 @@ class FivePaisaClient: - LOGIN_ROUTE = "https://Openapi.5paisa.com/VendorsAPI/Service1.svc/V2/LoginRequestMobileNewbyEmail" + LOGIN_ROUTE = "https://Openapi.5paisa.com/VendorsAPI/Service1.svc/V3/LoginRequestMobileNewbyEmail" MARGIN_ROUTE = "https://Openapi.5paisa.com/VendorsAPI/Service1.svc/V3/Margin" ORDER_BOOK_ROUTE = "https://Openapi.5paisa.com/VendorsAPI/Service1.svc/V2/OrderBook" @@ -46,7 +46,7 @@ def login(self): self.payload["body"]["Email_id"] = secret_email self.payload["body"]["Password"] = secret_passwd self.payload["body"]["My2PIN"] = secret_dob - self.payload["head"]["requestCode"] = "5PLoginV2" + self.payload["head"]["requestCode"] = "5PLoginV3" res = self._login_request(self.LOGIN_ROUTE) message = res["body"]["Message"] if message == "": diff --git a/setup.py b/setup.py index bc3edd52..4da99607 100644 --- a/setup.py +++ b/setup.py @@ -47,6 +47,6 @@ test_suite='tests', tests_require=test_requirements, url='https://github.com/5paisa/py5paisa', - version='0.1.6', + version='0.1.7', zip_safe=False, ) From aa1adda1fcac5ce3f522c95d0ebcd6098ebad32c Mon Sep 17 00:00:00 2001 From: invinciblycool Date: Sun, 28 Mar 2021 14:39:06 +0530 Subject: [PATCH 003/335] fix: auth issue --- py5paisa/const.py | 4 ++-- py5paisa/py5paisa.py | 14 ++++++++------ setup.py | 2 +- 3 files changed, 11 insertions(+), 9 deletions(-) diff --git a/py5paisa/const.py b/py5paisa/const.py index c65924a6..78998766 100644 --- a/py5paisa/const.py +++ b/py5paisa/const.py @@ -34,8 +34,8 @@ { "Email_id": "", "Password": "", - "LocalIP": "", - "PublicIP": "", + "LocalIP": "192.168.10.10", + "PublicIP": "192.168.10.10", "HDSerailNumber": "", "MACAddress": "", "MachineID": "039377", diff --git a/py5paisa/py5paisa.py b/py5paisa/py5paisa.py index 4ce1f932..5c0ae1c1 100644 --- a/py5paisa/py5paisa.py +++ b/py5paisa/py5paisa.py @@ -1,6 +1,6 @@ import requests from .auth import EncryptionClient -from .const import GENERIC_PAYLOAD, HEADERS, NEXT_DAY_TIMESTAMP, TODAY_TIMESTAMP +from .const import GENERIC_PAYLOAD, LOGIN_PAYLOAD, HEADERS, NEXT_DAY_TIMESTAMP, TODAY_TIMESTAMP from .conf import APP_SOURCE from .order import Order, RequestList, OrderType, OrderFor from .logging import log_response @@ -35,6 +35,7 @@ def __init__(self, email=None, passwd=None, dob=None): self.passwd = passwd self.dob = dob self.payload = GENERIC_PAYLOAD + self.login_payload = LOGIN_PAYLOAD self.client_code = None self.session = requests.Session() @@ -43,10 +44,10 @@ def login(self): secret_email = encryption_client.encrypt(self.email) secret_passwd = encryption_client.encrypt(self.passwd) secret_dob = encryption_client.encrypt(self.dob) - self.payload["body"]["Email_id"] = secret_email - self.payload["body"]["Password"] = secret_passwd - self.payload["body"]["My2PIN"] = secret_dob - self.payload["head"]["requestCode"] = "5PLoginV3" + self.login_payload["body"]["Email_id"] = secret_email + self.login_payload["body"]["Password"] = secret_passwd + self.login_payload["body"]["My2PIN"] = secret_dob + self.login_payload["head"]["requestCode"] = "5PLoginV3" res = self._login_request(self.LOGIN_ROUTE) message = res["body"]["Message"] if message == "": @@ -68,7 +69,8 @@ def positions(self): return self._user_info_request("POSITIONS") def _login_request(self, route): - res = self.session.post(route, json=self.payload, headers=HEADERS) + res = self.session.post( + route, json=self.login_payload, headers=HEADERS) return res.json() def _set_client_code(self, client_code): diff --git a/setup.py b/setup.py index 4da99607..06265cd6 100644 --- a/setup.py +++ b/setup.py @@ -47,6 +47,6 @@ test_suite='tests', tests_require=test_requirements, url='https://github.com/5paisa/py5paisa', - version='0.1.7', + version='0.1.8', zip_safe=False, ) From 95b1f82c576e9de6b10880d06a532b9c5ed5576c Mon Sep 17 00:00:00 2001 From: invinciblycool Date: Sun, 28 Mar 2021 14:41:18 +0530 Subject: [PATCH 004/335] bump python version to 3.8.8 --- .github/workflows/pythonpublish.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/pythonpublish.yml b/.github/workflows/pythonpublish.yml index 5fdb9e42..26804a1f 100644 --- a/.github/workflows/pythonpublish.yml +++ b/.github/workflows/pythonpublish.yml @@ -15,7 +15,7 @@ jobs: - name: Set up Python uses: actions/setup-python@v1 with: - python-version: '3.8.6' + python-version: '3.8.8' - name: Install dependencies run: | python -m pip install --upgrade pip From 0b29b0a7c25689e7843ea3841979745dd9b42fa4 Mon Sep 17 00:00:00 2001 From: invinciblycool Date: Sun, 28 Mar 2021 14:53:12 +0530 Subject: [PATCH 005/335] add badges --- README.md | 3 +++ 1 file changed, 3 insertions(+) diff --git a/README.md b/README.md index d5cc5118..c65c593a 100644 --- a/README.md +++ b/README.md @@ -2,6 +2,9 @@ Python SDK for 5paisa APIs natively written in VB .NET +![PyPI](https://img.shields.io/pypi/v/py5paisa) +![GitHub Workflow Status (branch)](https://img.shields.io/github/workflow/status/5paisa/py5paisa/Publish%20package/master) + ![5paisa logo](./docs/images/5-paisa-img.jpg) #### Documentation From 1b85ab9d463b1ca4af339c9f927b2593118cdb7b Mon Sep 17 00:00:00 2001 From: 5paisa <65898074+5paisa@users.noreply.github.com> Date: Fri, 16 Apr 2021 16:38:48 +0530 Subject: [PATCH 006/335] Update order.py Updated order class . --- py5paisa/order.py | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/py5paisa/order.py b/py5paisa/order.py index 375d11d2..e1cbf289 100644 --- a/py5paisa/order.py +++ b/py5paisa/order.py @@ -52,20 +52,20 @@ class AHPlaced(Enum): class Order: - def __init__(self, order_type: OrderType, scrip_code: int, quantity: int, order_for: OrderFor = OrderFor.PLACE, exchange: Exchange = Exchange.BSE, - exchange_segment: ExchangeSegment = ExchangeSegment.CASH, price: float = 0.0, order_id: int = 0, - atmarket: bool = True, remote_order_id: int = 1, exch_order_id: int = 0, + def __init__(self, order_type: str, scrip_code: int, quantity: int, exchange: str, + exchange_segment: str, price: float ,is_intraday: bool , atmarket: bool ,order_id: int = 0, + remote_order_id: int = 1, exch_order_id: int = 0, order_for: OrderFor = OrderFor.PLACE, stoploss_price: float = 0, is_stoploss_order: bool = False, ioc_order: bool = False, - is_intraday: bool = False, is_vtd: bool = False, vtd: str = f"/Date({NEXT_DAY_TIMESTAMP})/", + is_vtd: bool = False, vtd: str = f"/Date({NEXT_DAY_TIMESTAMP})/", ahplaced: AHPlaced = AHPlaced.NORMAL_ORDER, public_ip: str = '192.168.1.1', order_validity: OrderValidity = OrderValidity.DAY, traded_qty: int = 0): self.order_for = order_for.value - self.exchange = exchange.value - self.exchange_segment = exchange_segment.value + self.exchange = exchange + self.exchange_segment = exchange_segment self.price = price self.order_id = order_id - self.order_type = order_type.value + self.order_type = order_type self.quantity = quantity self.scrip_code = scrip_code self.atmarket = atmarket From 8fb6d2b6773c6fad5fb2094083646fed43761cf4 Mon Sep 17 00:00:00 2001 From: 5paisa <65898074+5paisa@users.noreply.github.com> Date: Fri, 16 Apr 2021 16:43:29 +0530 Subject: [PATCH 007/335] Update py5paisa.py changed modify and cancel function. --- py5paisa/py5paisa.py | 13 ++++++------- 1 file changed, 6 insertions(+), 7 deletions(-) diff --git a/py5paisa/py5paisa.py b/py5paisa/py5paisa.py index 5c0ae1c1..f2a5d2f9 100644 --- a/py5paisa/py5paisa.py +++ b/py5paisa/py5paisa.py @@ -171,21 +171,20 @@ def place_order(self, order: Order): self.set_payload(order) return self.order_request("OP") - def modify_order(self, exch_order_id: str, traded_qty: int, scrip_code: int): + def modify_order(self, order: Order): """ Modifies an existing order - Only exch_order_id, traded_qty and scrip_code makes sense here. """ - order = Order(order_type=OrderType.BUY, scrip_code=scrip_code, - quantity=0, order_for=OrderFor.MODIFY, exch_order_id=exch_order_id, traded_qty=traded_qty) + self.set_payload(order) + self.payload["body"]["OrderFor"] = "M" return self.order_request("OP") - def cancel_order(self, exch_order_id: str, traded_qty: int, scrip_code: int): + def cancel_order(self,order_type:str, scrip_code:int, quantity:int,exchange:str,exchange_segment:str,exch_order_id:str): """ Cancels an existing order """ - order = Order(order_type=OrderType.BUY, scrip_code=scrip_code, - quantity=0, order_for=OrderFor.CANCEL, exch_order_id=exch_order_id, traded_qty=traded_qty) + order = Order(order_type=order_type, scrip_code=scrip_code, + quantity=quantity,exchange=exchange,exchange_segment=exchange_segment, exch_order_id=exch_order_id, price=0.0,atmarket=False,is_intraday=False,order_for=OrderFor.CANCEL) self.set_payload(order) return self.order_request("OP") From b1812c5848982cdfd2da08f3d95a69ddf533c696 Mon Sep 17 00:00:00 2001 From: 5paisa <65898074+5paisa@users.noreply.github.com> Date: Fri, 16 Apr 2021 16:47:48 +0530 Subject: [PATCH 008/335] Update README.md updated order placement ,modification and cancelation. --- README.md | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/README.md b/README.md index c65c593a..74dc0d51 100644 --- a/README.md +++ b/README.md @@ -82,8 +82,7 @@ NSE: https://nse-scrips.herokuapp.com/ from py5paisa.order import Order, OrderType, Exchange, ExchangeType -test_order = Order(order_type=OrderType.BUY, scrip_code=11111, quantity=10) - +test_order = Order(order_type='B',exchange='N',exchange_segment='C', scrip_code=1660, quantity=1, price=205,is_intraday=True,atmarket=False) client.place_order(test_order) ``` @@ -91,13 +90,14 @@ client.place_order(test_order) #### Modifying an order ```py -client.modify_order(exch_order_id="12345678", traded_qty=10, scrip_code=11111) +test_order = Order(order_type='B', scrip_code=1660, quantity=1, price=205,is_intraday=False,exchange='N',exchange_segment='C',atmarket=True,exch_order_id="12345678" ) +client.modify_order(test_order) ``` #### Canceling an order ```py -client.cancel_order(exch_order_id="12345678", traded_qty=10, scrip_code=11111) +client.cancel_order(order_type='B', scrip_code=1660, quantity=1,exchange='N',exchange_segment='C',exch_order_id='12345678') ``` #### Fetching Order Status and Trade Information From 45ac2c89aed387d048877e8fcc9430038750dbcf Mon Sep 17 00:00:00 2001 From: Rahul Tiwari Date: Fri, 16 Apr 2021 16:52:26 +0530 Subject: [PATCH 009/335] Correct installation command --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 74dc0d51..0c695d11 100644 --- a/README.md +++ b/README.md @@ -19,7 +19,7 @@ Read the docs hosted [here](https://5paisa.github.io/) ### Installation -`pip install pyp5aisa` +`pip install py5paisa` ### Usage From 39a2f6a72f5200f14c334d1478630c96875e451c Mon Sep 17 00:00:00 2001 From: 5paisa <65898074+5paisa@users.noreply.github.com> Date: Fri, 16 Apr 2021 16:55:24 +0530 Subject: [PATCH 010/335] Update README.md --- README.md | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index 0c695d11..287dc4ea 100644 --- a/README.md +++ b/README.md @@ -72,8 +72,11 @@ client.order_book() Scrip codes reference: +Note : Use these Links for getting scrip codes + BSE: https://www.bseindia.com/ -NSE: https://nse-scrips.herokuapp.com/ +NSE FO: https://www.5paisa.com/docs/default-source/scrip-master/contract.txt +NSE CASH : https://www.5paisa.com/docs/default-source/scrip-master/security.txt #### Placing an order From 5b6065f59e6cfdb21962cce80b4b52906cec9e65 Mon Sep 17 00:00:00 2001 From: 5paisa <65898074+5paisa@users.noreply.github.com> Date: Fri, 16 Apr 2021 17:15:29 +0530 Subject: [PATCH 011/335] Update setup.py --- setup.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/setup.py b/setup.py index 06265cd6..ab02bb46 100644 --- a/setup.py +++ b/setup.py @@ -47,6 +47,6 @@ test_suite='tests', tests_require=test_requirements, url='https://github.com/5paisa/py5paisa', - version='0.1.8', + version='0.1.9', zip_safe=False, ) From b40316fd6fdb679ab99ed05c3309a16c8fe48e1f Mon Sep 17 00:00:00 2001 From: 5paisa <65898074+5paisa@users.noreply.github.com> Date: Mon, 19 Apr 2021 14:46:14 +0530 Subject: [PATCH 012/335] added bo-co and market feed added bracket order ,cover order and market feed and changes in fetch trade information and order status --- py5paisa/py5paisa.py | 84 ++++++++++++++++++++++++++++++++++++++++---- 1 file changed, 77 insertions(+), 7 deletions(-) diff --git a/py5paisa/py5paisa.py b/py5paisa/py5paisa.py index f2a5d2f9..75ec1908 100644 --- a/py5paisa/py5paisa.py +++ b/py5paisa/py5paisa.py @@ -2,7 +2,7 @@ from .auth import EncryptionClient from .const import GENERIC_PAYLOAD, LOGIN_PAYLOAD, HEADERS, NEXT_DAY_TIMESTAMP, TODAY_TIMESTAMP from .conf import APP_SOURCE -from .order import Order, RequestList, OrderType, OrderFor +from .order import Order, bo_co_order, OrderType, OrderFor from .logging import log_response import datetime from typing import Union @@ -20,7 +20,12 @@ class FivePaisaClient: ORDER_PLACEMENT_ROUTE = "https://Openapi.5paisa.com/VendorsAPI/Service1.svc/V1/OrderRequest" ORDER_STATUS_ROUTE = "https://Openapi.5paisa.com/VendorsAPI/Service1.svc/OrderStatus" TRADE_INFO_ROUTE = "https://Openapi.5paisa.com/VendorsAPI/Service1.svc/TradeInformation" - + + BRACKET_MOD_ROUTE="https://openapi.5paisa.com/VendorsAPI/Service1.svc/ModifySMOOrder" + BRACKET_ORDER_ROUTE="https://openapi.5paisa.com/VendorsAPI/Service1.svc/SMOOrderRequest" + + MARKET_FEED_ROUTE="https://Openapi.5paisa.com/VendorsAPI/Service1.svc/MarketFeed" + MARGIN_REQUEST_CODE = "5PMarginV3" ORDER_BOOK_REQUEST_CODE = "5POrdBkV2" HOLDINGS_REQUEST_CODE = "5PHoldingV2" @@ -118,6 +123,19 @@ def order_request(self, req_type) -> None: elif req_type == "TI": url = self.TRADE_INFO_ROUTE self.payload["head"]["requestCode"] = "5PTrdInfo" + elif req_type == "MF": + url = self.MARKET_FEED_ROUTE + self.payload["head"]["requestCode"] = "5PMF" + self.payload["body"]["COUNT"]=self.client_code + elif req_type == "BM": + url = self.BRACKET_MOD_ROUTE + self.payload["head"]["requestCode"] = "5PSModMOOrd" + self.payload["body"]["legtype"]=0 + self.payload["body"]["TMOPartnerOrderID"]=0 + elif req_type == "BO": + url = self.BRACKET_ORDER_ROUTE + self.payload["head"]["requestCode"] = "5PSMOOrd" + self.payload["body"]["OrderRequesterCode"]=self.client_code else: raise Exception("Invalid request type!") @@ -126,13 +144,23 @@ def order_request(self, req_type) -> None: log_response(res["body"]["Message"]) return res["body"] - def fetch_order_status(self, req_list: RequestList) -> dict: - self.payload["body"]["OrdStatusReqList"] = req_list.orders + def fetch_order_status(self, req_list:list) : + self.payload["body"]["OrdStatusReqList"] = req_list return self.order_request("OS") - def fetch_trade_info(self, req_list: RequestList) -> dict: - self.payload["body"]["TradeDetailList"] = req_list.orders + def fetch_trade_info(self, req_list:list) : + self.payload["body"]["TradeInformationList"] = req_list return self.order_request("TI") + + def fetch_market_feed(self, req_list:list) : + """ + market feed api + """ + + self.payload["body"]["MarketFeedData"] = req_list + self.payload["body"]["ClientLoginType"] = 0 + self.payload["body"]["LastRequestTime"] = f"/Date({TODAY_TIMESTAMP})/" + self.payload["body"]["RefreshRa def set_payload(self, order: Order) -> None: self.payload["body"]["OrderFor"] = order.order_for @@ -151,7 +179,6 @@ def set_payload(self, order: Order) -> None: self.payload["body"]["DisQty"] = order.disqty self.payload["body"]["IsStopLossOrder"] = str( order.is_stoploss_order).lower() - self.payload["body"]["StopLossPrice"] = order.stoploss_price self.payload["body"]["IsVTD"] = str(order.is_vtd).lower() self.payload["body"]["IOCOrder"] = str(order.ioc_order).lower() self.payload["body"]["IsIntraday"] = str(order.is_intraday).lower() @@ -163,12 +190,43 @@ def set_payload(self, order: Order) -> None: self.payload["body"]["OrderRequesterCode"] = self.client_code self.payload["body"]["AppSource"] = APP_SOURCE self.payload["body"]["iOrderValidity"] = order.order_validity + + def set_payload_bo(self,boco:bo_co_order)-> None: + """ + this is for bo-co order placement + """ + self.payload["body"]["RequestType"] = boco.RequestType + self.payload["body"]["BuySell"] = boco.BuySell + self.payload["body"]["Qty"] = boco.Qty + self.payload["body"]["Exch"] = boco.Exch + self.payload["body"]["ExchType"] = boco.ExchType + self.payload["body"]["DisQty"] = boco.DisQty + self.payload["body"]["AtMarket"] = boco.AtMarket + self.payload["body"]["ExchOrderId"] = boco.ExchOrderId + self.payload["body"]["LimitPriceInitialOrder"] = boco.LimitPriceInitialOrder + self.payload["body"]["TriggerPriceInitialOrder"] = boco.TriggerPriceInitialOrder + self.payload["body"]["LimitPriceProfitOrder"] = boco.LimitPriceProfitOrder + self.payload["body"]["TriggerPriceForSL"] = boco.TriggerPriceForSL + self.payload["body"]["TrailingSL"] = boco.TrailingSL + self.payload["body"]["StopLoss"] = boco.StopLoss + self.payload["body"]["ScripCode"] = boco.scrip_code + self.payload["body"]["OrderFor"] = boco.order_for + self.payload["body"]["UniqueOrderIDNormal"] = boco.UniqueOrderIDNormal + self.payload["body"]["UniqueOrderIDSL"] = boco.UniqueOrderIDSL + self.payload["body"]["UniqueOrderIDLimit"] = boco.UniqueOrderIDLimit + self.payload["body"]["LocalOrderIDNormal"] = boco.LocalOrderIDNormal + self.payload["body"]["LocalOrderIDSL"] = boco.LocalOrderIDSL + self.payload["body"]["LocalOrderIDLimit"] = boco.LocalOrderIDLimit + self.payload["body"]["PublicIP"] = boco.public_ip + self.payload["body"]["AppSource"] = boco.app_source + self.payload["body"]["TradedQty"] = boco.traded_qty def place_order(self, order: Order): """ Places a fresh order """ self.set_payload(order) + self.payload["body"]["StopLossPrice"] = order.stoploss_price return self.order_request("OP") def modify_order(self, order: Order): @@ -177,7 +235,9 @@ def modify_order(self, order: Order): """ self.set_payload(order) + self.payload["body"]["StopLossPrice"] = order.stoploss_price self.payload["body"]["OrderFor"] = "M" + return self.order_request("OP") def cancel_order(self,order_type:str, scrip_code:int, quantity:int,exchange:str,exchange_segment:str,exch_order_id:str): @@ -187,4 +247,14 @@ def cancel_order(self,order_type:str, scrip_code:int, quantity:int,exchange:str, order = Order(order_type=order_type, scrip_code=scrip_code, quantity=quantity,exchange=exchange,exchange_segment=exchange_segment, exch_order_id=exch_order_id, price=0.0,atmarket=False,is_intraday=False,order_for=OrderFor.CANCEL) self.set_payload(order) + self.payload["body"]["StopLossPrice"] = order.stoploss_price return self.order_request("OP") + + def bo_order(self,boco:bo_co_order): + self.set_payload_bo(boco) + return self.order_request("BO") + + def mod_bo_order(self,order: Order): + self.set_payload(order) + self.payload["body"]["TriggerPriceForSL"] = order.stoploss_price + return self.order_request("BM") From cc026324270d72bec523bdd3dd78f8c1f8098d91 Mon Sep 17 00:00:00 2001 From: 5paisa <65898074+5paisa@users.noreply.github.com> Date: Mon, 19 Apr 2021 14:49:11 +0530 Subject: [PATCH 013/335] Update order.py --- py5paisa/order.py | 77 +++++++++++++++++++++++++---------------------- 1 file changed, 41 insertions(+), 36 deletions(-) diff --git a/py5paisa/order.py b/py5paisa/order.py index e1cbf289..c7dc26c9 100644 --- a/py5paisa/order.py +++ b/py5paisa/order.py @@ -84,40 +84,45 @@ def __init__(self, order_type: str, scrip_code: int, quantity: int, exchange: st self.traded_qty = traded_qty self.app_source = int(APP_SOURCE) +class bo_co_order: + + def __init__(self,scrip_code: int, Qty: int,LimitPriceInitialOrder:float,TriggerPriceInitialOrder:float + ,LimitPriceProfitOrder:float, + TriggerPriceForSL:float,TrailingSL:int=0,StopLoss:int=0, + LocalOrderIDNormal:int=0,LocalOrderIDSL:int=0,LocalOrderIDLimit:int=0, + public_ip: str = '192.168.1.1',traded_qty: int = 0,RequestType: str ='P',BuySell:OrderType=OrderType.BUY, + order_for: str="S",Exch: Exchange = Exchange.NSE, + ExchType: ExchangeSegment = ExchangeSegment.CASH,DisQty: int=0,ExchOrderId:str="0",AtMarket: bool = False,UniqueOrderIDNormal:str="", + UniqueOrderIDSL:str="",UniqueOrderIDLimit:str=""): + + + self.order_for = order_for + self.Exch = Exch.value + self.ExchType = ExchType.value + self.RequestType=RequestType + self.BuySell=BuySell.value + self.scrip_code=scrip_code + self.DisQty=DisQty + self.LimitPriceInitialOrder=LimitPriceInitialOrder + self.TriggerPriceInitialOrder=TriggerPriceInitialOrder + self.LimitPriceProfitOrder=LimitPriceProfitOrder + self.AtMarket=AtMarket + #self.LimitPriceForSL=LimitPriceForSL + self.TriggerPriceForSL=TriggerPriceForSL + self.TrailingSL=TrailingSL + self.StopLoss=StopLoss + self.UniqueOrderIDNormal=UniqueOrderIDNormal + self.UniqueOrderIDSL=UniqueOrderIDSL + self.UniqueOrderIDLimit=UniqueOrderIDLimit + self.LocalOrderIDNormal=LocalOrderIDNormal + self.LocalOrderIDSL=LocalOrderIDSL + self.LocalOrderIDLimit=LocalOrderIDLimit + self.public_ip=public_ip + self.ExchOrderId=ExchOrderId + self.app_source = int(APP_SOURCE) + self.traded_qty =traded_qty + self.Qty=Qty + + if LimitPriceProfitOrder==0: + self.order_for="C" -class OrderForStatus: - """ - Order class for representing an order for only Status and trade information request. - """ - - def __init__(self, exchange: Exchange, exchange_segment: ExchangeSegment, scrip_code: int, order_id: str) -> None: - self.exchange = exchange - self.exchange_segment = exchange_segment - self.scrip_code = scrip_code - self.order_id = order_id - - def __str__(self): - return f"Order -> Exchange:{self.exchange}, Type: {self.exchange_segment}, Scrip Code: {self.scrip_code}, Order ID: {self.order_id}" - - def __repr__(self) -> dict: - """ - Overriding repr to return dict which can be directly appended to the orders list. - """ - return { - "Exch": self.exchange, - "ExchSegment": self.exchange_segment, - "ScripCode": self.scrip_code, - "RemoteOrderID": self.order_id - } - - -class RequestList: - - def __init__(self): - self.orders = [] - - def add_order(self, order: OrderForStatus) -> None: - """ - Adds order to the RequestList object required in the request payload. - """ - self.orders.append(order.__repr__()) From 441e7f64b7a84fe76192f728770a6798f0efccab Mon Sep 17 00:00:00 2001 From: 5paisa <65898074+5paisa@users.noreply.github.com> Date: Mon, 19 Apr 2021 15:37:43 +0530 Subject: [PATCH 014/335] Co-Bo,market feed,OrderStatus,trade information update --- README.md | 57 +++++++++++++++++++++++++++++++++++++++++++++++++------ 1 file changed, 51 insertions(+), 6 deletions(-) diff --git a/README.md b/README.md index 287dc4ea..c349acfa 100644 --- a/README.md +++ b/README.md @@ -52,7 +52,16 @@ client.login() ``` After successful authentication, you should get a `Logged in!!` message +#### Market Feed +'''py +#NOTE : Symbol has to be in the same format as specified in the example below. + +req_list_=[{"Exch":"N","ExchType":"D","Symbol":"NIFTY 22 APR 2021 CE 15200.00","Expiry":"20210422","StrikePrice":"15200","OptionType":"CE"}, + {"Exch":"N","ExchType":"D","Symbol":"NIFTY 22 APR 2021 PE 15200.00","Expiry":"20210422","StrikePrice":"15200","OptionType":"PE"}] + +client.fetch_market_feed(req_list_) +''' #### Fetching user info ```py @@ -102,23 +111,59 @@ client.modify_order(test_order) ```py client.cancel_order(order_type='B', scrip_code=1660, quantity=1,exchange='N',exchange_segment='C',exch_order_id='12345678') ``` +#### Bracket Order + +'''py +#For placing Braket order + +test_order=bo_co_order(scrip_code=1660,BuySell='B',Qty=1, LimitPriceInitialOrder=204,TriggerPriceInitialOrder=0,LimitPriceProfitOrder=208.0,TriggerPriceForSL=202,RequestType='P',AtMarket=False) + +client.bo_order(test_order) + +#For Modifying Bracket Order only for Initial order (entry) +test_order=bo_co_order(scrip_code=1660,BuySell='B',Qty=1, LimitPriceInitialOrder=203,TriggerPriceInitialOrder=0,LimitPriceProfitOrder=208.0,TriggerPriceForSL=202,RequestType='M',AtMarket=False,ExchOrderId='12345678') + +client.bo_order(test_order) +#Note : For cover order just pass LimitPriceProfitOrder equal to Zero. + +#For Modifying LimitPriceProfitOrder +test_order=Order(order_type='S', scrip_code=1660, quantity=1, price=208.50,is_intraday=True,exchange='N',exchange_segment='C',atmarket=False,exch_order_id="12345678" ,order_for=OrderFor.MODIFY) + +client.mod_bo_order(test_order) + +#For Modifying TriggerPriceForSL +#test_order=Order(order_type='S', scrip_code=1660, quantity=1, price=0,is_intraday=True,exchange='N',exchange_segment='C',atmarket=True,exch_order_id="123456789" ,stoploss_price=201.50,is_stoploss_order=True,order_for=OrderFor.MODIFY) + +#Note : You have pass atmarket=true while modifying stoploss price, Pass ExchorderId for the particular leg to modify. +''' #### Fetching Order Status and Trade Information ```py -from py5paisa.order import OrderForStatus, Exchange, ExchangeType, RequestList +from py5paisa.order import Exchange, ExchangeType -test_order_status = OrderForStatus(exchange=Exchange.BSE, exchange_type=ExchangeType.CASH, scrip_code=500875, order_id=0) +req_list= [ -req_list = RequestList() -# Add multiple orders to the RequestList to know status of multiple orders at once. -req_list.add_order(test_order_status) + { + "Exch": "N", + "ExchType": "C", + "ScripCode": 20374, + "ExchOrderID": "1000000015310807" + }] # Fetches the trade details client.fetch_trade_info(req_list) +req_list_= [ + + { + "Exch": "N", + "ExchType": "C", + "ScripCode": 20374, + "RemoteOrderID": "90980441" + }] # Fetches the order status -client.fetch_order_status(req_list) +client.fetch_order_status(req_list_) ``` From 5566a9bb3f382d88ff975488831fbb4f00abcb4f Mon Sep 17 00:00:00 2001 From: 5paisa <65898074+5paisa@users.noreply.github.com> Date: Mon, 19 Apr 2021 15:40:04 +0530 Subject: [PATCH 015/335] Update README.md --- README.md | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/README.md b/README.md index c349acfa..09d9ed4b 100644 --- a/README.md +++ b/README.md @@ -54,14 +54,15 @@ client.login() After successful authentication, you should get a `Logged in!!` message #### Market Feed -'''py +```py #NOTE : Symbol has to be in the same format as specified in the example below. req_list_=[{"Exch":"N","ExchType":"D","Symbol":"NIFTY 22 APR 2021 CE 15200.00","Expiry":"20210422","StrikePrice":"15200","OptionType":"CE"}, {"Exch":"N","ExchType":"D","Symbol":"NIFTY 22 APR 2021 PE 15200.00","Expiry":"20210422","StrikePrice":"15200","OptionType":"PE"}] client.fetch_market_feed(req_list_) -''' +``` + #### Fetching user info ```py @@ -113,7 +114,7 @@ client.cancel_order(order_type='B', scrip_code=1660, quantity=1,exchange='N',exc ``` #### Bracket Order -'''py +```py #For placing Braket order test_order=bo_co_order(scrip_code=1660,BuySell='B',Qty=1, LimitPriceInitialOrder=204,TriggerPriceInitialOrder=0,LimitPriceProfitOrder=208.0,TriggerPriceForSL=202,RequestType='P',AtMarket=False) @@ -136,7 +137,8 @@ client.mod_bo_order(test_order) #test_order=Order(order_type='S', scrip_code=1660, quantity=1, price=0,is_intraday=True,exchange='N',exchange_segment='C',atmarket=True,exch_order_id="123456789" ,stoploss_price=201.50,is_stoploss_order=True,order_for=OrderFor.MODIFY) #Note : You have pass atmarket=true while modifying stoploss price, Pass ExchorderId for the particular leg to modify. -''' +``` + #### Fetching Order Status and Trade Information ```py From 4421f442a234c2eabc55c27604741c85f1bb4f32 Mon Sep 17 00:00:00 2001 From: 5paisa <65898074+5paisa@users.noreply.github.com> Date: Mon, 19 Apr 2021 15:42:56 +0530 Subject: [PATCH 016/335] Co-Bo,Market Feed,Order Status,TradeInfo update --- README.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/README.md b/README.md index 09d9ed4b..5f92dd96 100644 --- a/README.md +++ b/README.md @@ -136,6 +136,8 @@ client.mod_bo_order(test_order) #For Modifying TriggerPriceForSL #test_order=Order(order_type='S', scrip_code=1660, quantity=1, price=0,is_intraday=True,exchange='N',exchange_segment='C',atmarket=True,exch_order_id="123456789" ,stoploss_price=201.50,is_stoploss_order=True,order_for=OrderFor.MODIFY) +client.mod_bo_order(test_order) + #Note : You have pass atmarket=true while modifying stoploss price, Pass ExchorderId for the particular leg to modify. ``` From f75ba3593c81a4ac8a7d6379d5722af9e22d5d0d Mon Sep 17 00:00:00 2001 From: 5paisa <65898074+5paisa@users.noreply.github.com> Date: Mon, 19 Apr 2021 15:44:41 +0530 Subject: [PATCH 017/335] Update order.py --- py5paisa/order.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/py5paisa/order.py b/py5paisa/order.py index c7dc26c9..64e3d7b3 100644 --- a/py5paisa/order.py +++ b/py5paisa/order.py @@ -87,10 +87,10 @@ def __init__(self, order_type: str, scrip_code: int, quantity: int, exchange: st class bo_co_order: def __init__(self,scrip_code: int, Qty: int,LimitPriceInitialOrder:float,TriggerPriceInitialOrder:float - ,LimitPriceProfitOrder:float, + ,LimitPriceProfitOrder:float,BuySell:str, TriggerPriceForSL:float,TrailingSL:int=0,StopLoss:int=0, LocalOrderIDNormal:int=0,LocalOrderIDSL:int=0,LocalOrderIDLimit:int=0, - public_ip: str = '192.168.1.1',traded_qty: int = 0,RequestType: str ='P',BuySell:OrderType=OrderType.BUY, + public_ip: str = '192.168.1.1',traded_qty: int = 0,RequestType: str ='P', order_for: str="S",Exch: Exchange = Exchange.NSE, ExchType: ExchangeSegment = ExchangeSegment.CASH,DisQty: int=0,ExchOrderId:str="0",AtMarket: bool = False,UniqueOrderIDNormal:str="", UniqueOrderIDSL:str="",UniqueOrderIDLimit:str=""): @@ -100,7 +100,7 @@ def __init__(self,scrip_code: int, Qty: int,LimitPriceInitialOrder:float,Trigger self.Exch = Exch.value self.ExchType = ExchType.value self.RequestType=RequestType - self.BuySell=BuySell.value + self.BuySell=BuySell self.scrip_code=scrip_code self.DisQty=DisQty self.LimitPriceInitialOrder=LimitPriceInitialOrder From ec74f8c759323e1607a07a31fa711779406f1f09 Mon Sep 17 00:00:00 2001 From: 5paisa <65898074+5paisa@users.noreply.github.com> Date: Mon, 19 Apr 2021 15:54:42 +0530 Subject: [PATCH 018/335] Update setup.py --- setup.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/setup.py b/setup.py index ab02bb46..9db26dd2 100644 --- a/setup.py +++ b/setup.py @@ -47,6 +47,6 @@ test_suite='tests', tests_require=test_requirements, url='https://github.com/5paisa/py5paisa', - version='0.1.9', + version='0.2.0', zip_safe=False, ) From 92c30b00cad7fed842664b26aba01c86405a790f Mon Sep 17 00:00:00 2001 From: 5paisa <65898074+5paisa@users.noreply.github.com> Date: Mon, 19 Apr 2021 15:56:06 +0530 Subject: [PATCH 019/335] Update order.py --- py5paisa/order.py | 1 - 1 file changed, 1 deletion(-) diff --git a/py5paisa/order.py b/py5paisa/order.py index 64e3d7b3..57386ce2 100644 --- a/py5paisa/order.py +++ b/py5paisa/order.py @@ -107,7 +107,6 @@ def __init__(self,scrip_code: int, Qty: int,LimitPriceInitialOrder:float,Trigger self.TriggerPriceInitialOrder=TriggerPriceInitialOrder self.LimitPriceProfitOrder=LimitPriceProfitOrder self.AtMarket=AtMarket - #self.LimitPriceForSL=LimitPriceForSL self.TriggerPriceForSL=TriggerPriceForSL self.TrailingSL=TrailingSL self.StopLoss=StopLoss From a8e58face635f1237cd1f95e1757c2c144a92404 Mon Sep 17 00:00:00 2001 From: 5paisa <65898074+5paisa@users.noreply.github.com> Date: Mon, 19 Apr 2021 16:48:41 +0530 Subject: [PATCH 020/335] Update py5paisa.py --- py5paisa/py5paisa.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/py5paisa/py5paisa.py b/py5paisa/py5paisa.py index 75ec1908..38dbbc3a 100644 --- a/py5paisa/py5paisa.py +++ b/py5paisa/py5paisa.py @@ -160,7 +160,8 @@ def fetch_market_feed(self, req_list:list) : self.payload["body"]["MarketFeedData"] = req_list self.payload["body"]["ClientLoginType"] = 0 self.payload["body"]["LastRequestTime"] = f"/Date({TODAY_TIMESTAMP})/" - self.payload["body"]["RefreshRa + self.payload["body"]["RefreshRate"] = "H" + return self.order_request("MF") def set_payload(self, order: Order) -> None: self.payload["body"]["OrderFor"] = order.order_for From 59f3233ca5723746952c59b7ba1b979c6c00bc4b Mon Sep 17 00:00:00 2001 From: 5paisa <65898074+5paisa@users.noreply.github.com> Date: Mon, 19 Apr 2021 17:17:22 +0530 Subject: [PATCH 021/335] Update setup.py --- setup.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/setup.py b/setup.py index 9db26dd2..69128403 100644 --- a/setup.py +++ b/setup.py @@ -47,6 +47,6 @@ test_suite='tests', tests_require=test_requirements, url='https://github.com/5paisa/py5paisa', - version='0.2.0', + version='0.2.1', zip_safe=False, ) From 74b3c9d69ab77488958731c80e4fdd4534f56004 Mon Sep 17 00:00:00 2001 From: 5paisa <65898074+5paisa@users.noreply.github.com> Date: Thu, 22 Apr 2021 14:21:17 +0530 Subject: [PATCH 022/335] Update index.md --- docs/index.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/docs/index.md b/docs/index.md index 46a6df93..c7426ce9 100644 --- a/docs/index.md +++ b/docs/index.md @@ -8,4 +8,5 @@ Python SDK for 5paisa APIs natively written in VB .NET - Order placement, modification and cancellation - Fetching user info including holdings, positions, margin and order book. -- Fetching order status and trade information. \ No newline at end of file +- Fetching order status and trade information. +- Placing, modifying and deleting Bracket Order From 556b9f2ad8d3a4f684f58f7a6287fa2108fa6b4c Mon Sep 17 00:00:00 2001 From: 5paisa <65898074+5paisa@users.noreply.github.com> Date: Thu, 22 Apr 2021 15:46:47 +0530 Subject: [PATCH 023/335] Update README.md --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 5f92dd96..e6447efb 100644 --- a/README.md +++ b/README.md @@ -144,7 +144,7 @@ client.mod_bo_order(test_order) #### Fetching Order Status and Trade Information ```py -from py5paisa.order import Exchange, ExchangeType +from py5paisa.order import Exchange req_list= [ From d879427dd5d864413c63d11ab8f22a342b09e78f Mon Sep 17 00:00:00 2001 From: 5paisa <65898074+5paisa@users.noreply.github.com> Date: Tue, 27 Apr 2021 12:16:22 +0530 Subject: [PATCH 024/335] Update pythonpublish.yml --- .github/workflows/pythonpublish.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/pythonpublish.yml b/.github/workflows/pythonpublish.yml index 26804a1f..e748fc5b 100644 --- a/.github/workflows/pythonpublish.yml +++ b/.github/workflows/pythonpublish.yml @@ -15,7 +15,7 @@ jobs: - name: Set up Python uses: actions/setup-python@v1 with: - python-version: '3.8.8' + python-version: '3.8.9' - name: Install dependencies run: | python -m pip install --upgrade pip From e38e9b41150eae0c78c5bd97e097a76b54e9461d Mon Sep 17 00:00:00 2001 From: 5paisa <65898074+5paisa@users.noreply.github.com> Date: Tue, 4 May 2021 15:43:16 +0530 Subject: [PATCH 025/335] Delete docs directory --- docs/images/5-paisa-img.jpg | Bin 48489 -> 0 bytes docs/index.md | 12 ----- docs/installation.md | 7 --- docs/usage/auth.md | 30 ----------- docs/usage/cancel.md | 5 -- docs/usage/info.md | 38 -------------- docs/usage/modify.md | 5 -- docs/usage/place.md | 97 ------------------------------------ 8 files changed, 194 deletions(-) delete mode 100644 docs/images/5-paisa-img.jpg delete mode 100644 docs/index.md delete mode 100644 docs/installation.md delete mode 100644 docs/usage/auth.md delete mode 100644 docs/usage/cancel.md delete mode 100644 docs/usage/info.md delete mode 100644 docs/usage/modify.md delete mode 100644 docs/usage/place.md diff --git a/docs/images/5-paisa-img.jpg b/docs/images/5-paisa-img.jpg deleted file mode 100644 index 4fe224de3bc5e0464ab0eb3fbea874e5f8836c3c..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 48489 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¥Fwn4UK+E6+#J{F>7G^NAB^6-96@lw}rp&{p#3{ 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%0D%< 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 - -Configure these keys in a file named `keys.conf` in the same directory as your python script exists - -A sample `keys.conf` is given below: - -```conf -[KEYS] -APP_NAME=YOUR_APP_NAME_HERE -APP_SOURCE=YOUR_APP_SOURCE_HERE -USER_ID=YOUR_USER_ID_HERE -PASSWORD=YOUR_PASSWORD_HERE -USER_KEY=YOUR_USER_KEY_HERE -ENCRYPTION_KEY=YOUR_ENCRYPTION_KEY_HERE -``` - -#### Login - -```py -from py5paisa import FivePaisaClient - -client = FivePaisaClient(email="random_email@xyz.com", passwd="password", dob="YYYYMMDD") -client.login() -``` - -After successful authentication, you should get a `Logged in!!` message. \ No newline at end of file diff --git a/docs/usage/cancel.md b/docs/usage/cancel.md deleted file mode 100644 index b84e1a4d..00000000 --- a/docs/usage/cancel.md +++ /dev/null @@ -1,5 +0,0 @@ -## Canceling an order - -```py -client.cancel_order(exch_order_id="12345678", traded_qty=10, scrip_code=11111) -``` \ No newline at end of file diff --git a/docs/usage/info.md b/docs/usage/info.md deleted file mode 100644 index b88e5d24..00000000 --- a/docs/usage/info.md +++ /dev/null @@ -1,38 +0,0 @@ -### Information - -#### User information - -```py -# Fetches holdings -client.holdings() - -# Fetches margin -client.margin() - -# Fetches positions -client.positions() - -# Fetches the order book of the client -client.order_book() - -``` - - -#### Order Status and Trade Information - -```py -from py5paisa.order import OrderForStatus, Exchange, ExchangeType, RequestList - -test_order_status = OrderForStatus(exchange=Exchange.BSE, exchange_type=ExchangeType.CASH, scrip_code=500875, order_id=0) - -req_list = RequestList() -# Add multiple orders to the RequestList to know status of multiple orders at once. -req_list.add_order(test_order_status) - -# Fetches the trade details -client.fetch_trade_info(req_list) - -# Fetches the order status -client.fetch_order_status(req_list) - -``` \ No newline at end of file diff --git a/docs/usage/modify.md b/docs/usage/modify.md deleted file mode 100644 index 6511205c..00000000 --- a/docs/usage/modify.md +++ /dev/null @@ -1,5 +0,0 @@ -## Modifying an order - -```py -client.modify_order(exch_order_id="12345678", traded_qty=10, scrip_code=11111) -``` diff --git a/docs/usage/place.md b/docs/usage/place.md deleted file mode 100644 index 81e6e81b..00000000 --- a/docs/usage/place.md +++ /dev/null @@ -1,97 +0,0 @@ -## Placing an order - -```py -# Note: This is an indicative order. - -from py5paisa.order import Order, OrderType - -test_order = Order(order_type=OrderType.BUY, scrip_code=11111, quantity=10, price=123.0) - -client.place_order(test_order) - -``` - -#### Detailed Usage - -#### Placing an intraday order - -By default orders are delivery, to place an intraday order, pass `is_intraday=True` - -```py -from py5paisa.order import Order, OrderType - -test_order = Order(order_type=OrderType.BUY, scrip_code=11111, quantity=10, is_intraday=True, price=123.0) -client.place_order(test_order) -``` - -#### Placing a stoploss order - -By default orders are regular, to place a stoploss order, pass `is_stoploss_order=True` and `stoploss_price` which is the trigger price for your order. - -`stoploss_price` is a float and for Buy Stop loss, Trigger price should not be greater than Limit Price. And for Sell Stop Loss Order Trigger Price should not be less than Limit Price. - -```py -from py5paisa.order import Order, OrderType - -test_order = Order(order_type=OrderType.BUY, scrip_code=11111, quantity=10, is_stoploss=True, stoploss_price=123.0) -client.place_order(test_order) -``` - -#### Placing offline orders - -By default all orders are normal orders, pass `ahplaced=AHPlaced.AFTER_MARKET_CLOSED` to place offline orders. - -```py -from py5paisa.order import Order, OrderType, AHPlaced - -test_order = Order(order_type=OrderType.BUY, scrip_code=11111, quantity=10, ahplaced=AHPlaced.AFTER_MARKET_CLOSED) -client.place_order(test_order) -``` - -#### Enums - -Following are the enums which can be imported and used for placing more complex orders. - - -```py -class Exchange(Enum): - - NSE = "N" - BSE = "B" - MCX = "M" -``` - -```py -class ExchangeSegment(Enum): - - CASH = "C" - DERIVATIVE = "D" - CURRENCY = "U" -``` - -```py -class OrderType(Enum): - - BUY = "BUY" - SELL = "SELL" -``` - -```py -class OrderValidity(Enum): - - DAY = 0 - GTD = 1 - GTC = 2 - IOC = 3 - EOS = 4 - FOK = 6 -``` - -```py -class AHPlaced(Enum): - - AFTER_MARKET_CLOSED = "Y" - NORMAL_ORDER = "N" -``` - -[Source](https://github.com/5paisa/py5paisa/blob/master/py5paisa/order.py) \ No newline at end of file From 7d1e7c6cd26f28066d9c99f5ab12fe6e67ffe30d Mon Sep 17 00:00:00 2001 From: 5paisa <65898074+5paisa@users.noreply.github.com> Date: Tue, 4 May 2021 15:44:01 +0530 Subject: [PATCH 026/335] Add files via upload --- docs/images/5-paisa-img.jpg | Bin 0 -> 48489 bytes docs/index.md | 13 ++++++ docs/installation.md | 7 +++ docs/usage/auth.md | 30 +++++++++++++ docs/usage/bo.md | 31 ++++++++++++++ docs/usage/cancel.md | 5 +++ docs/usage/info.md | 47 +++++++++++++++++++++ docs/usage/mf.md | 10 +++++ docs/usage/modify.md | 6 +++ docs/usage/place.md | 82 ++++++++++++++++++++++++++++++++++++ 10 files changed, 231 insertions(+) create mode 100644 docs/images/5-paisa-img.jpg create mode 100644 docs/index.md create mode 100644 docs/installation.md create mode 100644 docs/usage/auth.md create mode 100644 docs/usage/bo.md create mode 100644 docs/usage/cancel.md create mode 100644 docs/usage/info.md create mode 100644 docs/usage/mf.md create mode 100644 docs/usage/modify.md create mode 100644 docs/usage/place.md diff --git a/docs/images/5-paisa-img.jpg b/docs/images/5-paisa-img.jpg new file mode 100644 index 0000000000000000000000000000000000000000..4fe224de3bc5e0464ab0eb3fbea874e5f8836c3c GIT binary patch literal 48489 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¥Fwn4UK+E6+#J{F>7G^NAB^6-96@lw}rp&{p#3{ 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%0D%< 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 + +Configure these keys in a file named `keys.conf` in the same directory as your python script exists + +A sample `keys.conf` is given below: + +```conf +[KEYS] +APP_NAME=YOUR_APP_NAME_HERE +APP_SOURCE=YOUR_APP_SOURCE_HERE +USER_ID=YOUR_USER_ID_HERE +PASSWORD=YOUR_PASSWORD_HERE +USER_KEY=YOUR_USER_KEY_HERE +ENCRYPTION_KEY=YOUR_ENCRYPTION_KEY_HERE +``` + +#### Login + +```py +from py5paisa import FivePaisaClient + +client = FivePaisaClient(email="random_email@xyz.com", passwd="password", dob="YYYYMMDD") +client.login() +``` + +After successful authentication, you should get a `Logged in!!` message. \ No newline at end of file diff --git a/docs/usage/bo.md b/docs/usage/bo.md new file mode 100644 index 00000000..2ea59720 --- /dev/null +++ b/docs/usage/bo.md @@ -0,0 +1,31 @@ +#### Bracket Order + + +For placing Braket order +```py +test_order=bo_co_order(scrip_code=1660,BuySell='B',Qty=1, LimitPriceInitialOrder=204,TriggerPriceInitialOrder=0,LimitPriceProfitOrder=208.0,TriggerPriceForSL=202,RequestType='P',AtMarket=False) + +client.bo_order(test_order) +``` +For Modifying Bracket Order only for Initial order (entry) +```py +test_order=bo_co_order(scrip_code=1660,BuySell='B',Qty=1, LimitPriceInitialOrder=203,TriggerPriceInitialOrder=0,LimitPriceProfitOrder=208.0,TriggerPriceForSL=202,RequestType='M',AtMarket=False,ExchOrderId='12345678') + +client.bo_order(test_order) + +#Note : For cover order just pass LimitPriceProfitOrder equal to Zero. +``` +For Modifying LimitPriceProfitOrder +```py +test_order=Order(order_type='S', scrip_code=1660, quantity=1, price=208.50,is_intraday=True,exchange='N',exchange_segment='C',atmarket=False,exch_order_id="12345678" ,order_for=OrderFor.MODIFY) + +client.mod_bo_order(test_order) +``` +For Modifying TriggerPriceForSL +```py +test_order=Order(order_type='S', scrip_code=1660, quantity=1, price=0,is_intraday=True,exchange='N',exchange_segment='C',atmarket=True,exch_order_id="123456789" ,stoploss_price=201.50,is_stoploss_order=True,order_for=OrderFor.MODIFY) + +client.mod_bo_order(test_order) + +#Note : You have pass atmarket=true while modifying stoploss price, Pass ExchorderId for the particular leg to modify. +``` diff --git a/docs/usage/cancel.md b/docs/usage/cancel.md new file mode 100644 index 00000000..2f93f53a --- /dev/null +++ b/docs/usage/cancel.md @@ -0,0 +1,5 @@ +## Canceling an order + +```py +client.cancel_order(order_type='B', scrip_code=1660, quantity=1,exchange='N',exchange_segment='C',exch_order_id='12345678') +``` \ No newline at end of file diff --git a/docs/usage/info.md b/docs/usage/info.md new file mode 100644 index 00000000..700e12f5 --- /dev/null +++ b/docs/usage/info.md @@ -0,0 +1,47 @@ +### Information + +#### User information + +```py +# Fetches holdings +client.holdings() + +# Fetches margin +client.margin() + +# Fetches positions +client.positions() + +# Fetches the order book of the client +client.order_book() + +``` + + +#### Order Status and Trade Information + +```py + +req_list= [ + + { + "Exch": "N", + "ExchType": "C", + "ScripCode": 20374, + "ExchOrderID": "1000000015310807" + }] + +# Fetches the trade details +client.fetch_trade_info(req_list) + +req_list_= [ + + { + "Exch": "N", + "ExchType": "C", + "ScripCode": 20374, + "RemoteOrderID": "90980441" + }] +# Fetches the order status +client.fetch_order_status(req_list_) +``` \ No newline at end of file diff --git a/docs/usage/mf.md b/docs/usage/mf.md new file mode 100644 index 00000000..7998d222 --- /dev/null +++ b/docs/usage/mf.md @@ -0,0 +1,10 @@ +#### Market Feed + +```py +# NOTE : Symbol has to be in the same format as specified in the example below. + +req_list_=[{"Exch":"N","ExchType":"D","Symbol":"NIFTY 22 APR 2021 CE 15200.00","Expiry":"20210422","StrikePrice":"15200","OptionType":"CE"}, + {"Exch":"N","ExchType":"D","Symbol":"NIFTY 22 APR 2021 PE 15200.00","Expiry":"20210422","StrikePrice":"15200","OptionType":"PE"}] + +client.fetch_market_feed(req_list_) +``` diff --git a/docs/usage/modify.md b/docs/usage/modify.md new file mode 100644 index 00000000..77fa67ae --- /dev/null +++ b/docs/usage/modify.md @@ -0,0 +1,6 @@ +## Modifying an order + +```py +test_order = Order(order_type='B', scrip_code=1660, quantity=1, price=205,is_intraday=False,exchange='N',exchange_segment='C',atmarket=True,exch_order_id="12345678" ) +client.modify_order(test_order) +``` diff --git a/docs/usage/place.md b/docs/usage/place.md new file mode 100644 index 00000000..6fbe1222 --- /dev/null +++ b/docs/usage/place.md @@ -0,0 +1,82 @@ +## Placing an order + +```py +from py5paisa.order import Order, OrderType, Exchange + +test_order = Order(order_type='B',exchange='N',exchange_segment='C', scrip_code=1660, quantity=1, price=205,is_intraday=True,atmarket=False) +client.place_order(test_order) + +``` + +#### Detailed Usage + + +#### Placing a stoploss order + +By default orders are regular, to place a stoploss order, pass `is_stoploss_order=True` and `stoploss_price` which is the trigger price for your order. + +`stoploss_price` is a float and for Buy Stop loss, Trigger price should not be greater than Limit Price. And for Sell Stop Loss Order Trigger Price should not be less than Limit Price. + +```py +from py5paisa.order import Order, OrderType +test_order = Order(order_type='B',exchange='N',exchange_segment='C', scrip_code=1660, quantity=1, price=208,is_intraday=True,atmarket=False, is_stoploss_order=True, stoploss_price=207.5) + +``` + +#### Placing offline orders + +By default all orders are normal orders, pass `ahplaced=AHPlaced.AFTER_MARKET_CLOSED` to place offline orders. + +```py +from py5paisa.order import Order, OrderType, AHPlaced +test_order = Order(order_type='B',exchange='N',exchange_segment='C', scrip_code=1660, quantity=1, price=205,is_intraday=False,atmarket=False, ahplaced=AHPlaced.AFTER_MARKET_CLOSED) + +``` + +#### Enums + +Following are the enums which can be imported and used for placing more complex orders. + + +```py +class Exchange(Enum): + + NSE = "N" + BSE = "B" + MCX = "M" +``` + +```py +class ExchangeSegment(Enum): + + CASH = "C" + DERIVATIVE = "D" + CURRENCY = "U" +``` + +```py +class OrderType(Enum): + + BUY = "BUY" + SELL = "SELL" +``` + +```py +class OrderValidity(Enum): + + DAY = 0 + GTD = 1 + GTC = 2 + IOC = 3 + EOS = 4 + FOK = 6 +``` + +```py +class AHPlaced(Enum): + + AFTER_MARKET_CLOSED = "Y" + NORMAL_ORDER = "N" +``` + +[Source](https://github.com/5paisa/py5paisa/blob/master/py5paisa/order.py) \ No newline at end of file From a678fffe276bef7c8fa9623df864e4ad4be56b74 Mon Sep 17 00:00:00 2001 From: 5paisa <65898074+5paisa@users.noreply.github.com> Date: Tue, 4 May 2021 15:45:00 +0530 Subject: [PATCH 027/335] Update bo.md --- docs/usage/bo.md | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/docs/usage/bo.md b/docs/usage/bo.md index 2ea59720..01ca1ad8 100644 --- a/docs/usage/bo.md +++ b/docs/usage/bo.md @@ -7,13 +7,15 @@ test_order=bo_co_order(scrip_code=1660,BuySell='B',Qty=1, LimitPriceInitialOrder client.bo_order(test_order) ``` +Note : For cover order just pass LimitPriceProfitOrder equal to Zero. + For Modifying Bracket Order only for Initial order (entry) ```py test_order=bo_co_order(scrip_code=1660,BuySell='B',Qty=1, LimitPriceInitialOrder=203,TriggerPriceInitialOrder=0,LimitPriceProfitOrder=208.0,TriggerPriceForSL=202,RequestType='M',AtMarket=False,ExchOrderId='12345678') client.bo_order(test_order) -#Note : For cover order just pass LimitPriceProfitOrder equal to Zero. + ``` For Modifying LimitPriceProfitOrder ```py From 7cdf097a1b807a8c84968cac8971a17eedcd29ed Mon Sep 17 00:00:00 2001 From: 5paisa <65898074+5paisa@users.noreply.github.com> Date: Wed, 5 May 2021 16:00:00 +0530 Subject: [PATCH 028/335] Update const.py --- py5paisa/const.py | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/py5paisa/const.py b/py5paisa/const.py index 78998766..78c67fe3 100644 --- a/py5paisa/const.py +++ b/py5paisa/const.py @@ -45,6 +45,24 @@ "ConnectionType": "1" } } +LOGIN_CHECK_PAYLOAD={ + "head" : { + "requestCode":"5PLoginCheck", + "key":USER_KEY, + "appVer":"1.0", + "appName":APP_NAME, + "osName":"WEB", + "LoginId":"" + }, + "body":{ + "RegistrationID":"" + } + } + +WS_PAYLOAD={"Method":"", + "Operation":"", + "ClientCode":"", + "MarketFeedData":""} TODAY_TIMESTAMP = int(datetime.datetime.today().timestamp()) NEXT_DAY_TIMESTAMP = int( From 8ddab32c4778d62786b537df587d7064c86e7cf0 Mon Sep 17 00:00:00 2001 From: 5paisa <65898074+5paisa@users.noreply.github.com> Date: Wed, 5 May 2021 16:12:01 +0530 Subject: [PATCH 029/335] Update py5paisa.py --- py5paisa/py5paisa.py | 65 ++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 62 insertions(+), 3 deletions(-) diff --git a/py5paisa/py5paisa.py b/py5paisa/py5paisa.py index 38dbbc3a..3022a33b 100644 --- a/py5paisa/py5paisa.py +++ b/py5paisa/py5paisa.py @@ -1,11 +1,13 @@ import requests from .auth import EncryptionClient -from .const import GENERIC_PAYLOAD, LOGIN_PAYLOAD, HEADERS, NEXT_DAY_TIMESTAMP, TODAY_TIMESTAMP +from .const import GENERIC_PAYLOAD, LOGIN_PAYLOAD, HEADERS, NEXT_DAY_TIMESTAMP, TODAY_TIMESTAMP,LOGIN_CHECK_PAYLOAD,WS_PAYLOAD from .conf import APP_SOURCE from .order import Order, bo_co_order, OrderType, OrderFor from .logging import log_response import datetime from typing import Union +import json +import websocket class FivePaisaClient: @@ -41,7 +43,12 @@ def __init__(self, email=None, passwd=None, dob=None): self.dob = dob self.payload = GENERIC_PAYLOAD self.login_payload = LOGIN_PAYLOAD + self.login_check_payload= LOGIN_CHECK_PAYLOAD + self.ws_payload=WS_PAYLOAD self.client_code = None + self.Jwt_token = None + self.Aspx_auth = None + self.web_url= None self.session = requests.Session() def login(self): @@ -74,8 +81,11 @@ def positions(self): return self._user_info_request("POSITIONS") def _login_request(self, route): - res = self.session.post( - route, json=self.login_payload, headers=HEADERS) + res = self.session.post(route, json=self.login_payload, headers=HEADERS) + session_cookies = res.cookies + cookies_dictionary = session_cookies.get_dict() + self.Jwt_token=cookies_dictionary['JwtToken'] + #print(self.Jwt_token) return res.json() def _set_client_code(self, client_code): @@ -259,3 +269,52 @@ def mod_bo_order(self,order: Order): self.set_payload(order) self.payload["body"]["TriggerPriceForSL"] = order.stoploss_price return self.order_request("BM") + + def Request_Feed(self,Method:str,Operation:str,req_list:list): + Method_dict={"mf":"MarketFeedV3"} + Operation_dict={"s":"Subscribe","u":"Unsubscribe"} + + self.ws_payload['Method']=Method_dict[Method] + self.ws_payload['Operation']=Operation_dict[Operation] + self.ws_payload['ClientCode']=self.client_code + self.ws_payload['MarketFeedData']=req_list + return self.ws_payload + def Streming_data(self,wsPayload : dict): + self.web_url=f'wss://openfeed.5paisa.com/Feeds/api/chat?Value1={self.Jwt_token}|{self.client_code}' + auth=self.Login_check() + + def on_message(ws, message): + print(message) + + def on_error(ws, error): + print(error) + + def on_close(ws): + print("Streaming Stopped") + + def on_open(ws): + print("Streaming Started") + ws.send(json.dumps(wsPayload)) + + + ws = websocket.WebSocketApp(self.web_url, + on_open=on_open, + on_message = on_message, + on_error = on_error, + on_close = on_close, + cookie=auth) + + ws.run_forever() + + def Login_check(self): + self.login_check_payload["head"]["LoginId"]=self.client_code + self.login_check_payload["body"]["RegistrationID"]=self.Jwt_token + url=self.LOGIN_CHECK_ROUTE + resl=requests.post(url, json=self.login_check_payload,headers=HEADERS) + self.Aspx_auth = resl.cookies.get('.ASPXAUTH',domain='openfeed.5paisa.com') + + return f'.ASPXAUTH={self.Aspx_auth}' + + + + From 6d8aba9d103cd7759626bf5f41eedc3837c548c4 Mon Sep 17 00:00:00 2001 From: 5paisa <65898074+5paisa@users.noreply.github.com> Date: Wed, 5 May 2021 16:26:02 +0530 Subject: [PATCH 030/335] Update pythonpublish.yml --- .github/workflows/pythonpublish.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/pythonpublish.yml b/.github/workflows/pythonpublish.yml index e748fc5b..00335c72 100644 --- a/.github/workflows/pythonpublish.yml +++ b/.github/workflows/pythonpublish.yml @@ -15,7 +15,7 @@ jobs: - name: Set up Python uses: actions/setup-python@v1 with: - python-version: '3.8.9' + python-version: '3.8.10' - name: Install dependencies run: | python -m pip install --upgrade pip From a6821a804676c4bc2b5d4fc36b5f7c2a74f64f8d Mon Sep 17 00:00:00 2001 From: 5paisa <65898074+5paisa@users.noreply.github.com> Date: Wed, 5 May 2021 17:29:01 +0530 Subject: [PATCH 031/335] Update README.md --- README.md | 35 ++++++++++++++++++++++++++--------- 1 file changed, 26 insertions(+), 9 deletions(-) diff --git a/README.md b/README.md index e6447efb..b33fef7f 100644 --- a/README.md +++ b/README.md @@ -114,27 +114,30 @@ client.cancel_order(order_type='B', scrip_code=1660, quantity=1,exchange='N',exc ``` #### Bracket Order -```py -#For placing Braket order +For placing Braket order +```py test_order=bo_co_order(scrip_code=1660,BuySell='B',Qty=1, LimitPriceInitialOrder=204,TriggerPriceInitialOrder=0,LimitPriceProfitOrder=208.0,TriggerPriceForSL=202,RequestType='P',AtMarket=False) client.bo_order(test_order) - -#For Modifying Bracket Order only for Initial order (entry) +``` +For Modifying Bracket Order only for Initial order (entry) +```py test_order=bo_co_order(scrip_code=1660,BuySell='B',Qty=1, LimitPriceInitialOrder=203,TriggerPriceInitialOrder=0,LimitPriceProfitOrder=208.0,TriggerPriceForSL=202,RequestType='M',AtMarket=False,ExchOrderId='12345678') client.bo_order(test_order) #Note : For cover order just pass LimitPriceProfitOrder equal to Zero. - -#For Modifying LimitPriceProfitOrder +``` +For Modifying LimitPriceProfitOrder +```py test_order=Order(order_type='S', scrip_code=1660, quantity=1, price=208.50,is_intraday=True,exchange='N',exchange_segment='C',atmarket=False,exch_order_id="12345678" ,order_for=OrderFor.MODIFY) client.mod_bo_order(test_order) - -#For Modifying TriggerPriceForSL -#test_order=Order(order_type='S', scrip_code=1660, quantity=1, price=0,is_intraday=True,exchange='N',exchange_segment='C',atmarket=True,exch_order_id="123456789" ,stoploss_price=201.50,is_stoploss_order=True,order_for=OrderFor.MODIFY) +``` +For Modifying TriggerPriceForSL +```py +test_order=Order(order_type='S', scrip_code=1660, quantity=1, price=0,is_intraday=True,exchange='N',exchange_segment='C',atmarket=True,exch_order_id="123456789" ,stoploss_price=201.50,is_stoploss_order=True,order_for=OrderFor.MODIFY) client.mod_bo_order(test_order) @@ -170,6 +173,20 @@ req_list_= [ client.fetch_order_status(req_list_) ``` +#### Live Market Feed Streaming + +```py +req_list=[ + { "Exch":"N","ExchType":"C","ScripCode":1660}, + + ] + +dict1=Client.Request_Feed('mf','s',req_list) + +client.Streming_data(dict1) +``` + + #### TODO - Write tests. From 85a6693435b6a889e5f537f9d54be4152bddb746 Mon Sep 17 00:00:00 2001 From: 5paisa <65898074+5paisa@users.noreply.github.com> Date: Wed, 5 May 2021 17:33:06 +0530 Subject: [PATCH 032/335] Update setup.py --- setup.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/setup.py b/setup.py index 69128403..1980c485 100644 --- a/setup.py +++ b/setup.py @@ -47,6 +47,6 @@ test_suite='tests', tests_require=test_requirements, url='https://github.com/5paisa/py5paisa', - version='0.2.1', + version='0.2.2', zip_safe=False, ) From 380cc03f5b00844fdc46a8e115bce85c74d8fabe Mon Sep 17 00:00:00 2001 From: 5paisa <65898074+5paisa@users.noreply.github.com> Date: Thu, 6 May 2021 12:24:13 +0530 Subject: [PATCH 033/335] Update py5paisa.py --- py5paisa/py5paisa.py | 1 + 1 file changed, 1 insertion(+) diff --git a/py5paisa/py5paisa.py b/py5paisa/py5paisa.py index 3022a33b..a55f422e 100644 --- a/py5paisa/py5paisa.py +++ b/py5paisa/py5paisa.py @@ -27,6 +27,7 @@ class FivePaisaClient: BRACKET_ORDER_ROUTE="https://openapi.5paisa.com/VendorsAPI/Service1.svc/SMOOrderRequest" MARKET_FEED_ROUTE="https://Openapi.5paisa.com/VendorsAPI/Service1.svc/MarketFeed" + LOGIN_CHECK_ROUTE="https://openfeed.5paisa.com/Feeds/api/UserActivity/LoginCheck" MARGIN_REQUEST_CODE = "5PMarginV3" ORDER_BOOK_REQUEST_CODE = "5POrdBkV2" From c78e0df8372b7a51e6369e5a3f9bfc24704c2a1d Mon Sep 17 00:00:00 2001 From: 5paisa <65898074+5paisa@users.noreply.github.com> Date: Thu, 6 May 2021 12:25:43 +0530 Subject: [PATCH 034/335] Update setup.py --- setup.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/setup.py b/setup.py index 1980c485..3e5d98c9 100644 --- a/setup.py +++ b/setup.py @@ -47,6 +47,6 @@ test_suite='tests', tests_require=test_requirements, url='https://github.com/5paisa/py5paisa', - version='0.2.2', + version='0.2.3', zip_safe=False, ) From bc64213dd3aef50824e8640897626bce7f985957 Mon Sep 17 00:00:00 2001 From: 5paisa <65898074+5paisa@users.noreply.github.com> Date: Thu, 6 May 2021 15:57:15 +0530 Subject: [PATCH 035/335] Update README.md --- README.md | 3 +++ 1 file changed, 3 insertions(+) diff --git a/README.md b/README.md index b33fef7f..54dda089 100644 --- a/README.md +++ b/README.md @@ -15,7 +15,10 @@ Read the docs hosted [here](https://5paisa.github.io/) - Order placement, modification and cancellation - Fetching user info including holdings, positions, margin and order book. +- Fetching live market streaming. +- Placing, modifying and deleting Bracket Order. - Fetching order status and trade information. +- Getting live data streaming using websockets. ### Installation From 1312a6e28e438274c84ae0dd7c6d4e7a2ea6a823 Mon Sep 17 00:00:00 2001 From: 5paisa <65898074+5paisa@users.noreply.github.com> Date: Thu, 6 May 2021 16:06:57 +0530 Subject: [PATCH 036/335] Add files via upload --- docs/usage/websocket.md | 61 +++++++++++++++++++++++++++++++++++++++++ 1 file changed, 61 insertions(+) create mode 100644 docs/usage/websocket.md diff --git a/docs/usage/websocket.md b/docs/usage/websocket.md new file mode 100644 index 00000000..af5028d0 --- /dev/null +++ b/docs/usage/websocket.md @@ -0,0 +1,61 @@ +### Web Socket + +####For Subscribe ScripCode + +```py + +req_list=[ + { "Exch":"N","ExchType":"C","ScripCode":1660}, + + ] + +dict1=client.Request_Feed('mf','s',req_list) + + +``` + +####For Subscribe Multiple ScripCode + +```py + +req_list=[ + { "Exch":"N","ExchType":"C","ScripCode":1660}, + { "Exch":"N","ExchType":"D","ScripCode":61211} + ] + +dict1=client.Request_Feed('mf','s',req_list) + +``` + +####For Unsubscibe ScripCode + +```py + +req_list=[ + { "Exch":"N","ExchType":"C","ScripCode":1660} + ] + +dict1=client.Request_Feed('mf','u',req_list) + +``` + +####For Unsubscibe Multiple ScripCode + +```py + +req_list=[ + { "Exch":"N","ExchType":"C","ScripCode":1660}, + { "Exch":"N","ExchType":"D","ScripCode":61211} + ] + +dict1=client.Request_Feed('mf','u',req_list) + +``` + +####Start Streaming + +```py +client.Streming_data(dict1) + +# Note : Pass Dictionary in Parameter Field +``` \ No newline at end of file From fefc243cd772986eb07d282d257543a59edd22b3 Mon Sep 17 00:00:00 2001 From: 5paisa <65898074+5paisa@users.noreply.github.com> Date: Thu, 6 May 2021 16:07:33 +0530 Subject: [PATCH 037/335] Delete index.md --- docs/index.md | 13 ------------- 1 file changed, 13 deletions(-) delete mode 100644 docs/index.md diff --git a/docs/index.md b/docs/index.md deleted file mode 100644 index 4b0c707a..00000000 --- a/docs/index.md +++ /dev/null @@ -1,13 +0,0 @@ -# 5paisa Python SDK - -Python SDK for 5paisa APIs natively written in VB .NET - -![5paisa logo](images/5-paisa-img.jpg) - -### Features - -- Order placement, modification and cancellation -- Fetching user info including holdings, positions, margin and order book. -- Fetching live market streaming. -- Placing, modifying and deleting Bracket Order. -- Fetching order status and trade information. From a4ab1b9d1362b4b7cc3b32fcdbc06379c8fabeb6 Mon Sep 17 00:00:00 2001 From: 5paisa <65898074+5paisa@users.noreply.github.com> Date: Thu, 6 May 2021 16:07:53 +0530 Subject: [PATCH 038/335] Add files via upload --- docs/index.md | 14 ++++++++++++++ 1 file changed, 14 insertions(+) create mode 100644 docs/index.md diff --git a/docs/index.md b/docs/index.md new file mode 100644 index 00000000..441713fd --- /dev/null +++ b/docs/index.md @@ -0,0 +1,14 @@ +# 5paisa Python SDK + +Python SDK for 5paisa APIs natively written in VB .NET + +![5paisa logo](images/5-paisa-img.jpg) + +### Features + +- Order placement, modification and cancellation +- Fetching user info including holdings, positions, margin and order book. +- Fetching live market streaming. +- Placing, modifying and deleting Bracket Order. +- Fetching order status and trade information. +- Getting live data streaming using websocket. \ No newline at end of file From 39bf7dd81a226a262b813555a20f1806bb841c83 Mon Sep 17 00:00:00 2001 From: 5paisa <65898074+5paisa@users.noreply.github.com> Date: Thu, 6 May 2021 16:10:11 +0530 Subject: [PATCH 039/335] Delete mkdocs.yml --- mkdocs.yml | 17 ----------------- 1 file changed, 17 deletions(-) delete mode 100644 mkdocs.yml diff --git a/mkdocs.yml b/mkdocs.yml deleted file mode 100644 index 07f95d78..00000000 --- a/mkdocs.yml +++ /dev/null @@ -1,17 +0,0 @@ -site_name: py5paisa -nav: - - Home: 'index.md' - - Installation: 'installation.md' - - 'Usage': - - 'Authentication': 'usage/auth.md' - - 'Placing an order': 'usage/place.md' - - 'Modiying an order': 'usage/modify.md' - - 'Canceling an order': 'usage/cancel.md' - - 'Fetching information': 'usage/info.md' -theme: - name: material - features: - - instant -markdown_extensions: - - codehilite: - guess_lang: true From a2506690e2090556dd5c58ddc42e405a288406b8 Mon Sep 17 00:00:00 2001 From: 5paisa <65898074+5paisa@users.noreply.github.com> Date: Thu, 6 May 2021 16:10:27 +0530 Subject: [PATCH 040/335] Add files via upload --- mkdocs.yml | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) create mode 100644 mkdocs.yml diff --git a/mkdocs.yml b/mkdocs.yml new file mode 100644 index 00000000..8374ea06 --- /dev/null +++ b/mkdocs.yml @@ -0,0 +1,21 @@ +site_name: py5paisa +nav: + - Home: 'index.md' + - Installation: 'installation.md' + - 'Usage': + - 'Authentication': 'usage/auth.md' + - 'Placing an order': 'usage/place.md' + - 'Modiying an order': 'usage/modify.md' + - 'Canceling an order': 'usage/cancel.md' + - 'Fetching Market Feed Information': 'usage/mf.md' + - 'Placing a Bracket order': 'usage/bo.md' + - 'Fetching information': 'usage/info.md' + - 'Web Socket Live Streaming' : 'usage/websocket.md' + +theme: + name: material + features: + - instant +markdown_extensions: + - codehilite: + guess_lang: true From 5fc2d0afe918338e69d8bdb89169c787d197dc14 Mon Sep 17 00:00:00 2001 From: 5paisa <65898074+5paisa@users.noreply.github.com> Date: Fri, 7 May 2021 17:59:54 +0530 Subject: [PATCH 041/335] Update py5paisa.py --- py5paisa/py5paisa.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/py5paisa/py5paisa.py b/py5paisa/py5paisa.py index a55f422e..9e73f21c 100644 --- a/py5paisa/py5paisa.py +++ b/py5paisa/py5paisa.py @@ -272,7 +272,7 @@ def mod_bo_order(self,order: Order): return self.order_request("BM") def Request_Feed(self,Method:str,Operation:str,req_list:list): - Method_dict={"mf":"MarketFeedV3"} + Method_dict={"mf":"MarketFeedV3","md":"MarketDepthService","oi":"GetScripInfoForFuture"} Operation_dict={"s":"Subscribe","u":"Unsubscribe"} self.ws_payload['Method']=Method_dict[Method] From 83810fab8e9c0ca9226cf1970bec7b8275d6904c Mon Sep 17 00:00:00 2001 From: 5paisa <65898074+5paisa@users.noreply.github.com> Date: Fri, 7 May 2021 18:02:14 +0530 Subject: [PATCH 042/335] Update order.py --- py5paisa/order.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/py5paisa/order.py b/py5paisa/order.py index 57386ce2..b74d6b1c 100644 --- a/py5paisa/order.py +++ b/py5paisa/order.py @@ -92,13 +92,13 @@ def __init__(self,scrip_code: int, Qty: int,LimitPriceInitialOrder:float,Trigger LocalOrderIDNormal:int=0,LocalOrderIDSL:int=0,LocalOrderIDLimit:int=0, public_ip: str = '192.168.1.1',traded_qty: int = 0,RequestType: str ='P', order_for: str="S",Exch: Exchange = Exchange.NSE, - ExchType: ExchangeSegment = ExchangeSegment.CASH,DisQty: int=0,ExchOrderId:str="0",AtMarket: bool = False,UniqueOrderIDNormal:str="", + ExchType:str='C',DisQty: int=0,ExchOrderId:str="0",AtMarket: bool = False,UniqueOrderIDNormal:str="", UniqueOrderIDSL:str="",UniqueOrderIDLimit:str=""): self.order_for = order_for self.Exch = Exch.value - self.ExchType = ExchType.value + self.ExchType = ExchType self.RequestType=RequestType self.BuySell=BuySell self.scrip_code=scrip_code From 3e4f1007110612004e515aa0d45fc2f2ad463902 Mon Sep 17 00:00:00 2001 From: 5paisa <65898074+5paisa@users.noreply.github.com> Date: Fri, 7 May 2021 18:03:06 +0530 Subject: [PATCH 043/335] Update setup.py --- setup.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/setup.py b/setup.py index 3e5d98c9..8719d694 100644 --- a/setup.py +++ b/setup.py @@ -47,6 +47,6 @@ test_suite='tests', tests_require=test_requirements, url='https://github.com/5paisa/py5paisa', - version='0.2.3', + version='0.2.4', zip_safe=False, ) From b0c961b4cadc368411cebb1998be7b93cae7080d Mon Sep 17 00:00:00 2001 From: 5paisa <65898074+5paisa@users.noreply.github.com> Date: Fri, 7 May 2021 18:11:01 +0530 Subject: [PATCH 044/335] Update README.md --- README.md | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/README.md b/README.md index 54dda089..9865be19 100644 --- a/README.md +++ b/README.md @@ -118,12 +118,15 @@ client.cancel_order(order_type='B', scrip_code=1660, quantity=1,exchange='N',exc #### Bracket Order + For placing Braket order ```py test_order=bo_co_order(scrip_code=1660,BuySell='B',Qty=1, LimitPriceInitialOrder=204,TriggerPriceInitialOrder=0,LimitPriceProfitOrder=208.0,TriggerPriceForSL=202,RequestType='P',AtMarket=False) client.bo_order(test_order) ``` +Note:For placing Bracket order in FNO segment pass ExchType='D' + For Modifying Bracket Order only for Initial order (entry) ```py test_order=bo_co_order(scrip_code=1660,BuySell='B',Qty=1, LimitPriceInitialOrder=203,TriggerPriceInitialOrder=0,LimitPriceProfitOrder=208.0,TriggerPriceForSL=202,RequestType='M',AtMarket=False,ExchOrderId='12345678') @@ -132,6 +135,7 @@ client.bo_order(test_order) #Note : For cover order just pass LimitPriceProfitOrder equal to Zero. ``` + For Modifying LimitPriceProfitOrder ```py test_order=Order(order_type='S', scrip_code=1660, quantity=1, price=208.50,is_intraday=True,exchange='N',exchange_segment='C',atmarket=False,exch_order_id="12345678" ,order_for=OrderFor.MODIFY) @@ -188,7 +192,13 @@ dict1=Client.Request_Feed('mf','s',req_list) client.Streming_data(dict1) ``` +Note: use the following abbreviations : +Market Feed=mf +Market Depth (upto 5)=md +Open Interest=oi +Subscribe= s +Unsubscribe=u #### TODO From 4e0cc49f291f0606c46deb2fe7323e5262a529f9 Mon Sep 17 00:00:00 2001 From: 5paisa <65898074+5paisa@users.noreply.github.com> Date: Fri, 7 May 2021 18:13:44 +0530 Subject: [PATCH 045/335] Update README.md --- README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.md b/README.md index 9865be19..6a9a581a 100644 --- a/README.md +++ b/README.md @@ -193,6 +193,7 @@ dict1=Client.Request_Feed('mf','s',req_list) client.Streming_data(dict1) ``` Note: use the following abbreviations : + Market Feed=mf Market Depth (upto 5)=md Open Interest=oi From 70d416547a26588d2ee8ce5099ed3ddcbc76314c Mon Sep 17 00:00:00 2001 From: 5paisa <65898074+5paisa@users.noreply.github.com> Date: Fri, 7 May 2021 18:14:47 +0530 Subject: [PATCH 046/335] Update README.md --- README.md | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index 6a9a581a..64d4206e 100644 --- a/README.md +++ b/README.md @@ -192,13 +192,16 @@ dict1=Client.Request_Feed('mf','s',req_list) client.Streming_data(dict1) ``` -Note: use the following abbreviations : +Note: Use the following abbreviations : Market Feed=mf + Market Depth (upto 5)=md + Open Interest=oi Subscribe= s + Unsubscribe=u From 27a39ccb5bfdd82bd05354b9a45cef5fefa30004 Mon Sep 17 00:00:00 2001 From: 5paisa <65898074+5paisa@users.noreply.github.com> Date: Fri, 14 May 2021 12:36:45 +0530 Subject: [PATCH 047/335] Update order.py --- py5paisa/order.py | 22 +++++++++++----------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/py5paisa/order.py b/py5paisa/order.py index b74d6b1c..48d203ab 100644 --- a/py5paisa/order.py +++ b/py5paisa/order.py @@ -54,13 +54,13 @@ class Order: def __init__(self, order_type: str, scrip_code: int, quantity: int, exchange: str, exchange_segment: str, price: float ,is_intraday: bool , atmarket: bool ,order_id: int = 0, - remote_order_id: int = 1, exch_order_id: int = 0, order_for: OrderFor = OrderFor.PLACE, + remote_order_id: int = 1, exch_order_id: int = 0, order_for: str = 'P', stoploss_price: float = 0, is_stoploss_order: bool = False, ioc_order: bool = False, is_vtd: bool = False, vtd: str = f"/Date({NEXT_DAY_TIMESTAMP})/", - ahplaced: AHPlaced = AHPlaced.NORMAL_ORDER, public_ip: str = '192.168.1.1', - order_validity: OrderValidity = OrderValidity.DAY, traded_qty: int = 0): + ahplaced: str= 'N', public_ip: str = '192.168.1.1', + order_validity: int =0, traded_qty: int = 0): - self.order_for = order_for.value + self.order_for = order_for self.exchange = exchange self.exchange_segment = exchange_segment self.price = price @@ -78,26 +78,26 @@ def __init__(self, order_type: str, scrip_code: int, quantity: int, exchange: st self.is_intraday = is_intraday self.is_vtd = is_vtd self.vtd = vtd - self.ahplaced = ahplaced.value + self.ahplaced = ahplaced self.public_ip = public_ip - self.order_validity = order_validity.value + self.order_validity = order_validity self.traded_qty = traded_qty self.app_source = int(APP_SOURCE) class bo_co_order: def __init__(self,scrip_code: int, Qty: int,LimitPriceInitialOrder:float,TriggerPriceInitialOrder:float - ,LimitPriceProfitOrder:float,BuySell:str, + ,LimitPriceProfitOrder:float,BuySell:str,Exch: str,ExchType: str,RequestType: str, TriggerPriceForSL:float,TrailingSL:int=0,StopLoss:int=0, LocalOrderIDNormal:int=0,LocalOrderIDSL:int=0,LocalOrderIDLimit:int=0, - public_ip: str = '192.168.1.1',traded_qty: int = 0,RequestType: str ='P', - order_for: str="S",Exch: Exchange = Exchange.NSE, - ExchType:str='C',DisQty: int=0,ExchOrderId:str="0",AtMarket: bool = False,UniqueOrderIDNormal:str="", + public_ip: str = '192.168.1.1',traded_qty: int = 0, + order_for: str="S", + DisQty: int=0,ExchOrderId:str="0",AtMarket: bool = False,UniqueOrderIDNormal:str="", UniqueOrderIDSL:str="",UniqueOrderIDLimit:str=""): self.order_for = order_for - self.Exch = Exch.value + self.Exch = Exch self.ExchType = ExchType self.RequestType=RequestType self.BuySell=BuySell From 11255c5de450b9bea9259c572055d4ef795879bc Mon Sep 17 00:00:00 2001 From: 5paisa <65898074+5paisa@users.noreply.github.com> Date: Fri, 14 May 2021 12:38:34 +0530 Subject: [PATCH 048/335] Update py5paisa.py --- py5paisa/py5paisa.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/py5paisa/py5paisa.py b/py5paisa/py5paisa.py index 9e73f21c..1956efc2 100644 --- a/py5paisa/py5paisa.py +++ b/py5paisa/py5paisa.py @@ -86,7 +86,7 @@ def _login_request(self, route): session_cookies = res.cookies cookies_dictionary = session_cookies.get_dict() self.Jwt_token=cookies_dictionary['JwtToken'] - #print(self.Jwt_token) + return res.json() def _set_client_code(self, client_code): @@ -257,7 +257,7 @@ def cancel_order(self,order_type:str, scrip_code:int, quantity:int,exchange:str, Cancels an existing order """ order = Order(order_type=order_type, scrip_code=scrip_code, - quantity=quantity,exchange=exchange,exchange_segment=exchange_segment, exch_order_id=exch_order_id, price=0.0,atmarket=False,is_intraday=False,order_for=OrderFor.CANCEL) + quantity=quantity,exchange=exchange,exchange_segment=exchange_segment, exch_order_id=exch_order_id, price=0.0,atmarket=False,is_intraday=False,order_for='C') self.set_payload(order) self.payload["body"]["StopLossPrice"] = order.stoploss_price return self.order_request("OP") From d3976b539d4af9734cce7abc28ba16faed5c4659 Mon Sep 17 00:00:00 2001 From: 5paisa <65898074+5paisa@users.noreply.github.com> Date: Fri, 14 May 2021 12:44:09 +0530 Subject: [PATCH 049/335] Update README.md --- README.md | 16 ++++++++++++---- 1 file changed, 12 insertions(+), 4 deletions(-) diff --git a/README.md b/README.md index 64d4206e..8866a4b7 100644 --- a/README.md +++ b/README.md @@ -101,6 +101,14 @@ from py5paisa.order import Order, OrderType, Exchange, ExchangeType test_order = Order(order_type='B',exchange='N',exchange_segment='C', scrip_code=1660, quantity=1, price=205,is_intraday=True,atmarket=False) client.place_order(test_order) +``` +#### Placing offline orders + +By default all orders are normal orders, pass `ahplaced=Y` to place offline orders. + +```py +from py5paisa.order import Order, OrderType, AHPlaced +test_order = Order(order_type='B',exchange='N',exchange_segment='C', scrip_code=1660, quantity=1, price=205,is_intraday=False,atmarket=False, ahplaced='Y') ``` #### Modifying an order @@ -121,7 +129,7 @@ client.cancel_order(order_type='B', scrip_code=1660, quantity=1,exchange='N',exc For placing Braket order ```py -test_order=bo_co_order(scrip_code=1660,BuySell='B',Qty=1, LimitPriceInitialOrder=204,TriggerPriceInitialOrder=0,LimitPriceProfitOrder=208.0,TriggerPriceForSL=202,RequestType='P',AtMarket=False) +test_order=bo_co_order(scrip_code=1660,BuySell='B',Qty=1, LimitPriceInitialOrder=205,TriggerPriceInitialOrder=0,LimitPriceProfitOrder=215.0,TriggerPriceForSL=203,ExchType='C',Exch='N',RequestType='P',AtMarket=False) client.bo_order(test_order) ``` @@ -129,7 +137,7 @@ Note:For placing Bracket order in FNO segment pass ExchType='D' For Modifying Bracket Order only for Initial order (entry) ```py -test_order=bo_co_order(scrip_code=1660,BuySell='B',Qty=1, LimitPriceInitialOrder=203,TriggerPriceInitialOrder=0,LimitPriceProfitOrder=208.0,TriggerPriceForSL=202,RequestType='M',AtMarket=False,ExchOrderId='12345678') +test_order=bo_co_order(scrip_code=1660,BuySell='B',Qty=1, LimitPriceInitialOrder=203,TriggerPriceInitialOrder=0,LimitPriceProfitOrder=208.0,TriggerPriceForSL=202,ExchType='C',Exch='N',RequestType='M',AtMarket=False,ExchOrderId='12345678') client.bo_order(test_order) @@ -138,13 +146,13 @@ client.bo_order(test_order) For Modifying LimitPriceProfitOrder ```py -test_order=Order(order_type='S', scrip_code=1660, quantity=1, price=208.50,is_intraday=True,exchange='N',exchange_segment='C',atmarket=False,exch_order_id="12345678" ,order_for=OrderFor.MODIFY) +test_order=Order(order_type='S', scrip_code=1660, quantity=1, price=208.50,is_intraday=True,exchange='N',exchange_segment='C',atmarket=False,exch_order_id="12345678" ,order_for='M') client.mod_bo_order(test_order) ``` For Modifying TriggerPriceForSL ```py -test_order=Order(order_type='S', scrip_code=1660, quantity=1, price=0,is_intraday=True,exchange='N',exchange_segment='C',atmarket=True,exch_order_id="123456789" ,stoploss_price=201.50,is_stoploss_order=True,order_for=OrderFor.MODIFY) +test_order=Order(order_type='S', scrip_code=1660, quantity=1, price=0,is_intraday=True,exchange='N',exchange_segment='C',atmarket=True,exch_order_id="123456789" ,stoploss_price=201.50,is_stoploss_order=True,order_for='M') client.mod_bo_order(test_order) From 2419386e52d5bb26bc79138c6b2bf93a55ecfb94 Mon Sep 17 00:00:00 2001 From: 5paisa <65898074+5paisa@users.noreply.github.com> Date: Fri, 14 May 2021 12:45:17 +0530 Subject: [PATCH 050/335] Update setup.py --- setup.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/setup.py b/setup.py index 8719d694..27fda2f9 100644 --- a/setup.py +++ b/setup.py @@ -47,6 +47,6 @@ test_suite='tests', tests_require=test_requirements, url='https://github.com/5paisa/py5paisa', - version='0.2.4', + version='0.2.5', zip_safe=False, ) From 0dbb0fb427711bd1fb160626bf2d545bc1d4b1ec Mon Sep 17 00:00:00 2001 From: 5paisa <65898074+5paisa@users.noreply.github.com> Date: Fri, 14 May 2021 14:02:01 +0530 Subject: [PATCH 051/335] Update README.md --- README.md | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) diff --git a/README.md b/README.md index 8866a4b7..67628bdb 100644 --- a/README.md +++ b/README.md @@ -88,21 +88,19 @@ Scrip codes reference: Note : Use these Links for getting scrip codes BSE: https://www.bseindia.com/ -NSE FO: https://www.5paisa.com/docs/default-source/scrip-master/contract.txt -NSE CASH : https://www.5paisa.com/docs/default-source/scrip-master/security.txt - +Scrip Master - https://www.5paisa.com/docs/default-source/scrip-master/scripmaster-csv-format.csv #### Placing an order ```py # Note: This is an indicative order. -from py5paisa.order import Order, OrderType, Exchange, ExchangeType +from py5paisa.order import Order, OrderType, Exchange test_order = Order(order_type='B',exchange='N',exchange_segment='C', scrip_code=1660, quantity=1, price=205,is_intraday=True,atmarket=False) client.place_order(test_order) ``` -#### Placing offline orders +#### Placing offline orders (After Market Orders) By default all orders are normal orders, pass `ahplaced=Y` to place offline orders. @@ -165,7 +163,6 @@ client.mod_bo_order(test_order) from py5paisa.order import Exchange req_list= [ - { "Exch": "N", "ExchType": "C", From 13220f5bc50dc912d2a8de7122aa4ef1f3dcf8de Mon Sep 17 00:00:00 2001 From: 5paisa <65898074+5paisa@users.noreply.github.com> Date: Wed, 2 Jun 2021 17:42:38 +0530 Subject: [PATCH 052/335] Add files via upload --- py5paisa/strategy.py | 221 +++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 221 insertions(+) create mode 100644 py5paisa/strategy.py diff --git a/py5paisa/strategy.py b/py5paisa/strategy.py new file mode 100644 index 00000000..a9df9702 --- /dev/null +++ b/py5paisa/strategy.py @@ -0,0 +1,221 @@ +# These are standard + +from py5paisa import FivePaisaClient +from py5paisa.order import Order,bo_co_order +from .conf import user,passw,dob + +Client=FivePaisaClient(email=user, passwd=passw, dob=dob) +Client.login() + +#market feed api +# req_list_=[{"Exch":"N","ExchType":"D","Symbol":"NIFTY 20 MAY 2021 CE 14500.00","Expiry":"20210520","StrikePrice":"14500","OptionType":"CE"},{"Exch":"N","ExchType":"D","Symbol":"NIFTY 27 MAY 2021","Expiry":"20210527","StrikePrice":"0","OptionType":"XX"},{"Exch":"B","ExchType":"C","Symbol":"LT"},{"Exch":"M","ExchType":"D","Symbol":"GOLD"}] +# print(Client.fetch_market_feed(req_list_)) +# short_straddle("NIFTY",15000,75) + +class strategies: + # def __init__(self,symbol,strike,qty,expiry): + # self.symbol=symbol + # self.strike=strike + # self.qty=qty + # self.expiry=expiry + + def get_scripcode(self,symbol,strike,expiry,opt): + month={ + "01":'JAN', + "02":'FEB', + "03":'MAR', + "04":'APR', + "05":'MAY', + "06":'JUN', + "07":'JUL', + "08":'AUG', + "09":'SEP', + "10":'OCT', + "11":'NOV', + "12":'DEC' + } + date=expiry[6:] + mon=month[expiry[4:6]] + year=expiry[:4] + symbol=symbol.upper() + strike_f="{:.2f}".format(float(strike)) + sym=f'{symbol} {date} {mon} {year} {opt} {strike_f}' + print(sym) + req=[{"Exch":"N","ExchType":"D","Symbol":sym,"Expiry":expiry,"StrikePrice":strike,"OptionType":opt}] + res=Client.fetch_market_feed(req) + token=res['Data'][0]['Token'] + return token + + def intraday(self,intra): + if intra=='I': + return True + else: + return False + + def short_straddle(self,symbol,strike,qty,expiry,intra): + self.symbol=symbol + self.strike=strike + self.qty=qty + self.expiry=expiry + self.intra=intra + scrip=[] + options =['CE','PE'] + #Get Scrip code + for opt in options: + sc=self.get_scripcode(self.symbol,self.strike,self.expiry,opt) + scrip.append(sc) + + for s in scrip: + test_order = Order(order_type='S',exchange='N',exchange_segment='D', scrip_code=s, quantity=qty, price=0,is_intraday=self.intraday(self.intra),atmarket=True,) + order_status=Client.place_order(test_order) + print(order_status) + if order_status['Message']=='Success': + continue + else: + break + #short_strangle(self,"NIFTY",[15000,15200],qty,expiry) + def short_strangle(self,symbol,strike,qty,expiry,intra): + self.symbol=symbol + self.strike=strike + self.qty=qty + self.expiry=expiry + self.intra=intra + scrip=[] + i=0 + options =['PE','CE'] + #Get Scrip code + for opt in options: + sc=self.get_scripcode(self.symbol,self.strike[i],self.expiry,opt) + i=i+1 + scrip.append(sc) + + for s in scrip: + test_order = Order(order_type='S',exchange='N',exchange_segment='D', scrip_code=s, quantity=qty, price=0,is_intraday=self.intraday(self.intra),atmarket=True) + order_status=Client.place_order(test_order) + if order_status['Message']=='Success': + continue + else: + break + + def long_straddle(self,symbol,strike,qty,expiry,intra): + self.symbol=symbol + self.strike=strike + self.qty=qty + self.expiry=expiry + self.intra=intra + scrip=[] + options =['CE','PE'] + #Get Scrip code + for opt in options: + sc=self.get_scripcode(self.symbol,self.strike,self.expiry,opt) + scrip.append(sc) + + for s in scrip: + test_order = Order(order_type='B',exchange='N',exchange_segment='D', scrip_code=s, quantity=qty, price=0,is_intraday=self.intraday(self.intra),atmarket=True) + order_status=Client.place_order(test_order) + if order_status['Message']=='Success': + continue + else: + break + #long_strangle(self,"NIFTY",["15000","15200"],qty,expiry) + def long_strangle(self,symbol,strike,qty,expiry,intra): + self.symbol=symbol + self.strike=strike + self.qty=qty + self.expiry=expiry + self.intra=intra + scrip=[] + i=0 + options =['PE','CE'] + #Get Scrip code + for opt in options: + sc=self.get_scripcode(self.symbol,self.strike[i],self.expiry,opt) + i=i+1 + scrip.append(sc) + + for s in scrip: + test_order = Order(order_type='B',exchange='N',exchange_segment='D', scrip_code=s, quantity=qty, price=0,is_intraday=self.intraday(self.intra),atmarket=True) + order_status=Client.place_order(test_order) + if order_status['Message']=='Success': + continue + else: + break + #iron_fly(self,"NIFTY",["15000","15200"],qty,expiry,intra) + def iron_fly(self,symbol,buy_strike,sell_strike,qty,expiry,intra): + self.symbol=symbol + self.buy_strike=buy_strike + self.sell_strike=sell_strike + self.qty=qty + self.expiry=expiry + self.intra=intra + buy_scrip=[] + sell_scrip=[] + i=0 + options =['PE','CE'] + #Get Scrip code + for opt in options: + sc=self.get_scripcode(self.symbol,self.buy_strike[i],self.expiry,opt) + i=i+1 + buy_scrip.append(sc) + for opt in options: + sc=self.get_scripcode(self.symbol,self.sell_strike,self.expiry,opt) + sell_scrip.append(sc) + print(buy_scrip,sell_scrip) + for s in buy_scrip: + test_order = Order(order_type='B',exchange='N',exchange_segment='D', scrip_code=s, quantity=qty, price=0,is_intraday=self.intraday(self.intra),atmarket=True) + order_status=Client.place_order(test_order) + if order_status['Message']=='Success': + continue + else: + break + for s in sell_scrip: + test_order = Order(order_type='S',exchange='N',exchange_segment='D', scrip_code=s, quantity=qty, price=0,is_intraday=self.intraday(self.intra),atmarket=True) + order_status=Client.place_order(test_order) + if order_status['Message']=='Success': + continue + else: + break + + #iron_condor(,,,,,) + #iron_condor("NIFTY",["15000","15200"],["15000","15200"],"75","20210603",I/D) + def iron_condor(self,symbol,buy_strike,sell_strike,qty,expiry,intra): + self.symbol=symbol + self.buy_strike=buy_strike + self.sell_strike=sell_strike + self.qty=qty + self.expiry=expiry + self.intra=intra + buy_scrip=[] + sell_scrip=[] + i=0 + j=0 + options =['PE','CE'] + #Get Scrip code + for opt in options: + sc=self.get_scripcode(self.symbol,self.buy_strike[i],self.expiry,opt) + i=i+1 + buy_scrip.append(sc) + for opt in options: + sc=self.get_scripcode(self.symbol,self.sell_strike[j],self.expiry,opt) + j=j+1 + sell_scrip.append(sc) + print(buy_scrip,sell_scrip) + for s in buy_scrip: + test_order = Order(order_type='B',exchange='N',exchange_segment='D', scrip_code=s, quantity=qty, price=0,is_intraday=self.intraday(self.intra),atmarket=True,) + order_status=Client.place_order(test_order) + if order_status['Message']=='Success': + continue + else: + break + for s in sell_scrip: + test_order = Order(order_type='S',exchange='N',exchange_segment='D', scrip_code=s, quantity=qty, price=0,is_intraday=self.intraday(self.intra),atmarket=True) + order_status=Client.place_order(test_order) + if order_status['Message']=='Success': + continue + else: + break + + + +#ob.iron_condor("nifty",["15000","15600"],["15300","15400"],"75","20210603") +#ob.iron_fly("nifty",["15200","15600"],"15400","75","20210603") \ No newline at end of file From d62b5e80f2b67c41c18831da8b63bdf7e3de31d2 Mon Sep 17 00:00:00 2001 From: 5paisa <65898074+5paisa@users.noreply.github.com> Date: Wed, 2 Jun 2021 17:43:27 +0530 Subject: [PATCH 053/335] Update conf.py --- py5paisa/conf.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/py5paisa/conf.py b/py5paisa/conf.py index 1edb8628..b975bc52 100644 --- a/py5paisa/conf.py +++ b/py5paisa/conf.py @@ -16,3 +16,6 @@ PASSWORD = config["KEYS"]["PASSWORD"] USER_KEY = config["KEYS"]["USER_KEY"] ENCRYPTION_KEY = config["KEYS"]["ENCRYPTION_KEY"] +user=config["KEYS"]["user"] +passw=config["KEYS"]["pass"] +dob=config["KEYS"]["dob"] From 93dfb4bd4b4ed0526020242482d91a1b63a38046 Mon Sep 17 00:00:00 2001 From: 5paisa <65898074+5paisa@users.noreply.github.com> Date: Wed, 2 Jun 2021 18:00:01 +0530 Subject: [PATCH 054/335] Update README.md --- README.md | 24 ++++++++++++++++++++++++ 1 file changed, 24 insertions(+) diff --git a/README.md b/README.md index 67628bdb..8c4c10d7 100644 --- a/README.md +++ b/README.md @@ -42,6 +42,9 @@ USER_ID=YOUR_USER_ID_HERE PASSWORD=YOUR_PASSWORD_HERE USER_KEY=YOUR_USER_KEY_HERE ENCRYPTION_KEY=YOUR_ENCRYPTION_KEY_HERE +user=YOUR_EMAIL_ID +pass=YOUR_LOGIN_PASSWORD +dob=YOUR_DOB ``` @@ -209,6 +212,27 @@ Subscribe= s Unsubscribe=u +#### Strategy Execution + +Note: These single-commands are capable of trading multiple legs of pre-defined strategies. +Like :- Short/Long Straddles and Strangles, Iron Fly and Iron Condor (many more to come) +Please use these at your own risk. +```py +Create an Object:- +strategy=strategies() + +iron_condor(,,,,) +strategy.short_straddle("banknifty",['35300','37000'],'50','20210610','I') + +strategy.short_strangle("banknifty",['35300','37000'],'50','20210610','D') + +iron_condor(,,,,,) +strategy.iron_condor("NIFTY",["15000","15200"],["15100","15150"],"75","20210603","I") + +iron_condor(,,,,,) +strategy.iron_fly("NIFTY",["15000","15200"],"15100","75","20210610","I") + +``` #### TODO - Write tests. From 367f3a8e57f92443aca2bb80fdb42eb88a1c21f3 Mon Sep 17 00:00:00 2001 From: 5paisa <65898074+5paisa@users.noreply.github.com> Date: Wed, 2 Jun 2021 18:02:23 +0530 Subject: [PATCH 055/335] Update README.md --- README.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index 8c4c10d7..2ebfd33f 100644 --- a/README.md +++ b/README.md @@ -218,9 +218,10 @@ Note: These single-commands are capable of trading multiple legs of pre-defined Like :- Short/Long Straddles and Strangles, Iron Fly and Iron Condor (many more to come) Please use these at your own risk. ```py -Create an Object:- +#Create an Object:- strategy=strategies() +Use the following to execute the strategy (note:- they are executed at market price only) iron_condor(,,,,) strategy.short_straddle("banknifty",['35300','37000'],'50','20210610','I') From e6154859f4eb415d1e9ef7cfbdd780aedec63f2f Mon Sep 17 00:00:00 2001 From: 5paisa <65898074+5paisa@users.noreply.github.com> Date: Wed, 2 Jun 2021 18:03:15 +0530 Subject: [PATCH 056/335] Update README.md --- README.md | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/README.md b/README.md index 2ebfd33f..916c5a3c 100644 --- a/README.md +++ b/README.md @@ -221,16 +221,16 @@ Please use these at your own risk. #Create an Object:- strategy=strategies() -Use the following to execute the strategy (note:- they are executed at market price only) -iron_condor(,,,,) +#Use the following to execute the strategy (note:- they are executed at market price only) +#iron_condor(,,,,) strategy.short_straddle("banknifty",['35300','37000'],'50','20210610','I') strategy.short_strangle("banknifty",['35300','37000'],'50','20210610','D') -iron_condor(,,,,,) +#iron_condor(,,,,,) strategy.iron_condor("NIFTY",["15000","15200"],["15100","15150"],"75","20210603","I") -iron_condor(,,,,,) +#iron_condor(,,,,,) strategy.iron_fly("NIFTY",["15000","15200"],"15100","75","20210610","I") ``` From 7f38cbb7d7803f0b5a1ccdb7fc8d4b83030cd3b9 Mon Sep 17 00:00:00 2001 From: 5paisa <65898074+5paisa@users.noreply.github.com> Date: Wed, 2 Jun 2021 18:04:08 +0530 Subject: [PATCH 057/335] Update setup.py --- setup.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/setup.py b/setup.py index 27fda2f9..fa72cb8d 100644 --- a/setup.py +++ b/setup.py @@ -47,6 +47,6 @@ test_suite='tests', tests_require=test_requirements, url='https://github.com/5paisa/py5paisa', - version='0.2.5', + version='0.2.6', zip_safe=False, ) From 96459e6008265e41d3c6b4f5de2827f0a99ed25f Mon Sep 17 00:00:00 2001 From: 5paisa <65898074+5paisa@users.noreply.github.com> Date: Wed, 2 Jun 2021 18:14:15 +0530 Subject: [PATCH 058/335] Update README.md --- README.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/README.md b/README.md index 916c5a3c..c6cdbe5d 100644 --- a/README.md +++ b/README.md @@ -214,6 +214,8 @@ Unsubscribe=u #### Strategy Execution +from py5paisa.strategy import * + Note: These single-commands are capable of trading multiple legs of pre-defined strategies. Like :- Short/Long Straddles and Strangles, Iron Fly and Iron Condor (many more to come) Please use these at your own risk. From 5f1046bc28c44bfa1ca26970e7d9b6af2bf42770 Mon Sep 17 00:00:00 2001 From: 5paisa <65898074+5paisa@users.noreply.github.com> Date: Thu, 3 Jun 2021 12:28:37 +0530 Subject: [PATCH 059/335] Update strategy.py --- py5paisa/strategy.py | 73 ++++++++++++++++++++++++++------------------ 1 file changed, 44 insertions(+), 29 deletions(-) diff --git a/py5paisa/strategy.py b/py5paisa/strategy.py index a9df9702..df7a0cc4 100644 --- a/py5paisa/strategy.py +++ b/py5paisa/strategy.py @@ -1,4 +1,4 @@ -# These are standard +# These are standard strategies to be used at your own risk only after complete information. from py5paisa import FivePaisaClient from py5paisa.order import Order,bo_co_order @@ -7,17 +7,7 @@ Client=FivePaisaClient(email=user, passwd=passw, dob=dob) Client.login() -#market feed api -# req_list_=[{"Exch":"N","ExchType":"D","Symbol":"NIFTY 20 MAY 2021 CE 14500.00","Expiry":"20210520","StrikePrice":"14500","OptionType":"CE"},{"Exch":"N","ExchType":"D","Symbol":"NIFTY 27 MAY 2021","Expiry":"20210527","StrikePrice":"0","OptionType":"XX"},{"Exch":"B","ExchType":"C","Symbol":"LT"},{"Exch":"M","ExchType":"D","Symbol":"GOLD"}] -# print(Client.fetch_market_feed(req_list_)) -# short_straddle("NIFTY",15000,75) - class strategies: - # def __init__(self,symbol,strike,qty,expiry): - # self.symbol=symbol - # self.strike=strike - # self.qty=qty - # self.expiry=expiry def get_scripcode(self,symbol,strike,expiry,opt): month={ @@ -40,7 +30,6 @@ def get_scripcode(self,symbol,strike,expiry,opt): symbol=symbol.upper() strike_f="{:.2f}".format(float(strike)) sym=f'{symbol} {date} {mon} {year} {opt} {strike_f}' - print(sym) req=[{"Exch":"N","ExchType":"D","Symbol":sym,"Expiry":expiry,"StrikePrice":strike,"OptionType":opt}] res=Client.fetch_market_feed(req) token=res['Data'][0]['Token'] @@ -60,7 +49,6 @@ def short_straddle(self,symbol,strike,qty,expiry,intra): self.intra=intra scrip=[] options =['CE','PE'] - #Get Scrip code for opt in options: sc=self.get_scripcode(self.symbol,self.strike,self.expiry,opt) scrip.append(sc) @@ -73,8 +61,9 @@ def short_straddle(self,symbol,strike,qty,expiry,intra): continue else: break - #short_strangle(self,"NIFTY",[15000,15200],qty,expiry) + def short_strangle(self,symbol,strike,qty,expiry,intra): + strike.sort() self.symbol=symbol self.strike=strike self.qty=qty @@ -83,7 +72,6 @@ def short_strangle(self,symbol,strike,qty,expiry,intra): scrip=[] i=0 options =['PE','CE'] - #Get Scrip code for opt in options: sc=self.get_scripcode(self.symbol,self.strike[i],self.expiry,opt) i=i+1 @@ -105,7 +93,6 @@ def long_straddle(self,symbol,strike,qty,expiry,intra): self.intra=intra scrip=[] options =['CE','PE'] - #Get Scrip code for opt in options: sc=self.get_scripcode(self.symbol,self.strike,self.expiry,opt) scrip.append(sc) @@ -117,8 +104,9 @@ def long_straddle(self,symbol,strike,qty,expiry,intra): continue else: break - #long_strangle(self,"NIFTY",["15000","15200"],qty,expiry) + def long_strangle(self,symbol,strike,qty,expiry,intra): + strike.sort() self.symbol=symbol self.strike=strike self.qty=qty @@ -127,7 +115,6 @@ def long_strangle(self,symbol,strike,qty,expiry,intra): scrip=[] i=0 options =['PE','CE'] - #Get Scrip code for opt in options: sc=self.get_scripcode(self.symbol,self.strike[i],self.expiry,opt) i=i+1 @@ -140,8 +127,9 @@ def long_strangle(self,symbol,strike,qty,expiry,intra): continue else: break - #iron_fly(self,"NIFTY",["15000","15200"],qty,expiry,intra) + def iron_fly(self,symbol,buy_strike,sell_strike,qty,expiry,intra): + buy_strike.sort() self.symbol=symbol self.buy_strike=buy_strike self.sell_strike=sell_strike @@ -152,7 +140,6 @@ def iron_fly(self,symbol,buy_strike,sell_strike,qty,expiry,intra): sell_scrip=[] i=0 options =['PE','CE'] - #Get Scrip code for opt in options: sc=self.get_scripcode(self.symbol,self.buy_strike[i],self.expiry,opt) i=i+1 @@ -160,7 +147,6 @@ def iron_fly(self,symbol,buy_strike,sell_strike,qty,expiry,intra): for opt in options: sc=self.get_scripcode(self.symbol,self.sell_strike,self.expiry,opt) sell_scrip.append(sc) - print(buy_scrip,sell_scrip) for s in buy_scrip: test_order = Order(order_type='B',exchange='N',exchange_segment='D', scrip_code=s, quantity=qty, price=0,is_intraday=self.intraday(self.intra),atmarket=True) order_status=Client.place_order(test_order) @@ -176,9 +162,9 @@ def iron_fly(self,symbol,buy_strike,sell_strike,qty,expiry,intra): else: break - #iron_condor(,,,,,) - #iron_condor("NIFTY",["15000","15200"],["15000","15200"],"75","20210603",I/D) def iron_condor(self,symbol,buy_strike,sell_strike,qty,expiry,intra): + buy_strike.sort() + sell_strike.sort() self.symbol=symbol self.buy_strike=buy_strike self.sell_strike=sell_strike @@ -190,7 +176,6 @@ def iron_condor(self,symbol,buy_strike,sell_strike,qty,expiry,intra): i=0 j=0 options =['PE','CE'] - #Get Scrip code for opt in options: sc=self.get_scripcode(self.symbol,self.buy_strike[i],self.expiry,opt) i=i+1 @@ -199,7 +184,6 @@ def iron_condor(self,symbol,buy_strike,sell_strike,qty,expiry,intra): sc=self.get_scripcode(self.symbol,self.sell_strike[j],self.expiry,opt) j=j+1 sell_scrip.append(sc) - print(buy_scrip,sell_scrip) for s in buy_scrip: test_order = Order(order_type='B',exchange='N',exchange_segment='D', scrip_code=s, quantity=qty, price=0,is_intraday=self.intraday(self.intra),atmarket=True,) order_status=Client.place_order(test_order) @@ -214,8 +198,39 @@ def iron_condor(self,symbol,buy_strike,sell_strike,qty,expiry,intra): continue else: break - - -#ob.iron_condor("nifty",["15000","15600"],["15300","15400"],"75","20210603") -#ob.iron_fly("nifty",["15200","15600"],"15400","75","20210603") \ No newline at end of file + def call_calendar(self,symbol,strike,qty,expiry,intra): + self.symbol=symbol + self.strike=strike + self.qty=qty + self.expiry=expiry + self.intra=intra + scrip=[] + i=0 + options =['CE','CE'] + for opt in options: + sc=self.get_scripcode(self.symbol,self.strike,self.expiry[i],opt) + scrip.append(sc) + i=i+1 + test_order = Order(order_type='B',exchange='N',exchange_segment='D', scrip_code=scrip[0], quantity=qty, price=0,is_intraday=self.intraday(self.intra),atmarket=True) + order_status=Client.place_order(test_order) + test_order = Order(order_type='S',exchange='N',exchange_segment='D', scrip_code=scrip[1], quantity=qty, price=0,is_intraday=self.intraday(self.intra),atmarket=True) + order_status=Client.place_order(test_order) + + def put_calendar(self,symbol,strike,qty,expiry,intra): + self.symbol=symbol + self.strike=strike + self.qty=qty + self.expiry=expiry + self.intra=intra + scrip=[] + i=0 + options =['PE','PE'] + for opt in options: + sc=self.get_scripcode(self.symbol,self.strike,self.expiry[i],opt) + scrip.append(sc) + i=i+1 + test_order = Order(order_type='B',exchange='N',exchange_segment='D', scrip_code=scrip[0], quantity=qty, price=0,is_intraday=self.intraday(self.intra),atmarket=True) + order_status=Client.place_order(test_order) + test_order = Order(order_type='S',exchange='N',exchange_segment='D', scrip_code=scrip[1], quantity=qty, price=0,is_intraday=self.intraday(self.intra),atmarket=True) + order_status=Client.place_order(test_order) From ab4c38fe0d9971dc30cc55d68dc627d64d3a0fd2 Mon Sep 17 00:00:00 2001 From: 5paisa <65898074+5paisa@users.noreply.github.com> Date: Thu, 3 Jun 2021 12:34:49 +0530 Subject: [PATCH 060/335] Update README.md --- README.md | 28 ++++++++++++++++++---------- 1 file changed, 18 insertions(+), 10 deletions(-) diff --git a/README.md b/README.md index c6cdbe5d..218f0ab3 100644 --- a/README.md +++ b/README.md @@ -213,28 +213,36 @@ Subscribe= s Unsubscribe=u #### Strategy Execution - +```py +#Import strategy package from py5paisa.strategy import * - +``` Note: These single-commands are capable of trading multiple legs of pre-defined strategies. Like :- Short/Long Straddles and Strangles, Iron Fly and Iron Condor (many more to come) Please use these at your own risk. ```py #Create an Object:- strategy=strategies() - -#Use the following to execute the strategy (note:- they are executed at market price only) -#iron_condor(,,,,) +``` +Use the following to execute the strategy (note:- they are executed at market price only) +```py +#short_straddle(,,,,) strategy.short_straddle("banknifty",['35300','37000'],'50','20210610','I') - +``` +```py strategy.short_strangle("banknifty",['35300','37000'],'50','20210610','D') - +``` +```py #iron_condor(,,,,,) strategy.iron_condor("NIFTY",["15000","15200"],["15100","15150"],"75","20210603","I") - -#iron_condor(,,,,,) +``` +```py +#iron_fly(,,,,,) strategy.iron_fly("NIFTY",["15000","15200"],"15100","75","20210610","I") - +``` +```py +#call_calendar(,,,,) +ob.put_calendar("nifty",'15600','75',['20210603','20210610'],'I') ``` #### TODO From ccf72012206c77914fdcd86a1bf8059932a18636 Mon Sep 17 00:00:00 2001 From: 5paisa <65898074+5paisa@users.noreply.github.com> Date: Thu, 3 Jun 2021 12:39:35 +0530 Subject: [PATCH 061/335] Update README.md --- README.md | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/README.md b/README.md index 218f0ab3..2f9a187d 100644 --- a/README.md +++ b/README.md @@ -213,6 +213,15 @@ Subscribe= s Unsubscribe=u #### Strategy Execution +#### List Of Strategies Available + - Short Straddle + - Short Strangle + - Long Straddle + - Long Strangle + - Iron Fly(Butterfly) + - Iron Condor + - Call Calendar Spread + - Put Calendar Spread ```py #Import strategy package from py5paisa.strategy import * From 84a46c8e7878472a0ed1a2af1ddfbe54de950ab1 Mon Sep 17 00:00:00 2001 From: 5paisa <65898074+5paisa@users.noreply.github.com> Date: Thu, 3 Jun 2021 12:40:41 +0530 Subject: [PATCH 062/335] Update setup.py --- setup.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/setup.py b/setup.py index fa72cb8d..c5c5896e 100644 --- a/setup.py +++ b/setup.py @@ -47,6 +47,6 @@ test_suite='tests', tests_require=test_requirements, url='https://github.com/5paisa/py5paisa', - version='0.2.6', + version='0.2.7', zip_safe=False, ) From 937a9392707972503dd43cc528174f2a315bfcda Mon Sep 17 00:00:00 2001 From: 5paisa <65898074+5paisa@users.noreply.github.com> Date: Thu, 3 Jun 2021 14:25:37 +0530 Subject: [PATCH 063/335] Update README.md --- README.md | 21 ++++++++++++++++++--- 1 file changed, 18 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index 2f9a187d..2b833d3d 100644 --- a/README.md +++ b/README.md @@ -235,12 +235,22 @@ strategy=strategies() ``` Use the following to execute the strategy (note:- they are executed at market price only) ```py -#short_straddle(,,,,) -strategy.short_straddle("banknifty",['35300','37000'],'50','20210610','I') +#short_straddle(,,,,) +strategy.short_straddle("banknifty",'37000','50','20210610','I') ``` ```py +#short_strangle(,,,,) strategy.short_strangle("banknifty",['35300','37000'],'50','20210610','D') ``` +```py +#long_straddle(,,,,) +strategy.long_straddle("banknifty",'37000','50','20210610','I') +``` +```py +#long_strangle(,,,,) +strategy.long_strangle("banknifty",['35300','37000'],'50','20210610','D') +``` + ```py #iron_condor(,,,,,) strategy.iron_condor("NIFTY",["15000","15200"],["15100","15150"],"75","20210603","I") @@ -251,7 +261,12 @@ strategy.iron_fly("NIFTY",["15000","15200"],"15100","75","20210610","I") ``` ```py #call_calendar(,,,,) -ob.put_calendar("nifty",'15600','75',['20210603','20210610'],'I') +strategy.call_calendar("nifty",'15600','75',['20210603','20210610'],'I') +``` + +```py +#put_calendar(,,,,) +strategy.put_calendar("nifty",'15600','75',['20210603','20210610'],'I') ``` #### TODO From 26f7218ca2d5fc42aa7022ec847ae457d8845095 Mon Sep 17 00:00:00 2001 From: 5paisa <65898074+5paisa@users.noreply.github.com> Date: Fri, 18 Jun 2021 12:03:31 +0530 Subject: [PATCH 064/335] Delete py5paisa.py --- py5paisa/py5paisa.py | 321 ------------------------------------------- 1 file changed, 321 deletions(-) delete mode 100644 py5paisa/py5paisa.py diff --git a/py5paisa/py5paisa.py b/py5paisa/py5paisa.py deleted file mode 100644 index 1956efc2..00000000 --- a/py5paisa/py5paisa.py +++ /dev/null @@ -1,321 +0,0 @@ -import requests -from .auth import EncryptionClient -from .const import GENERIC_PAYLOAD, LOGIN_PAYLOAD, HEADERS, NEXT_DAY_TIMESTAMP, TODAY_TIMESTAMP,LOGIN_CHECK_PAYLOAD,WS_PAYLOAD -from .conf import APP_SOURCE -from .order import Order, bo_co_order, OrderType, OrderFor -from .logging import log_response -import datetime -from typing import Union -import json -import websocket - - -class FivePaisaClient: - - LOGIN_ROUTE = "https://Openapi.5paisa.com/VendorsAPI/Service1.svc/V3/LoginRequestMobileNewbyEmail" - - MARGIN_ROUTE = "https://Openapi.5paisa.com/VendorsAPI/Service1.svc/V3/Margin" - ORDER_BOOK_ROUTE = "https://Openapi.5paisa.com/VendorsAPI/Service1.svc/V2/OrderBook" - HOLDINGS_ROUTE = "https://openapi.5paisa.com/VendorsAPI/Service1.svc/V2/Holding" - POSITIONS_ROUTE = "https://Openapi.5paisa.com/VendorsAPI/Service1.svc/V1/NetPositionNetWise" - - ORDER_PLACEMENT_ROUTE = "https://Openapi.5paisa.com/VendorsAPI/Service1.svc/V1/OrderRequest" - ORDER_STATUS_ROUTE = "https://Openapi.5paisa.com/VendorsAPI/Service1.svc/OrderStatus" - TRADE_INFO_ROUTE = "https://Openapi.5paisa.com/VendorsAPI/Service1.svc/TradeInformation" - - BRACKET_MOD_ROUTE="https://openapi.5paisa.com/VendorsAPI/Service1.svc/ModifySMOOrder" - BRACKET_ORDER_ROUTE="https://openapi.5paisa.com/VendorsAPI/Service1.svc/SMOOrderRequest" - - MARKET_FEED_ROUTE="https://Openapi.5paisa.com/VendorsAPI/Service1.svc/MarketFeed" - LOGIN_CHECK_ROUTE="https://openfeed.5paisa.com/Feeds/api/UserActivity/LoginCheck" - - MARGIN_REQUEST_CODE = "5PMarginV3" - ORDER_BOOK_REQUEST_CODE = "5POrdBkV2" - HOLDINGS_REQUEST_CODE = "5PHoldingV2" - POSITIONS_REQUEST_CODE = "5PNPNWV1" - - def __init__(self, email=None, passwd=None, dob=None): - """ - Main constructor for client. - Expects user's email, password and date of birth in YYYYMMDD format. - """ - self.email = email - self.passwd = passwd - self.dob = dob - self.payload = GENERIC_PAYLOAD - self.login_payload = LOGIN_PAYLOAD - self.login_check_payload= LOGIN_CHECK_PAYLOAD - self.ws_payload=WS_PAYLOAD - self.client_code = None - self.Jwt_token = None - self.Aspx_auth = None - self.web_url= None - self.session = requests.Session() - - def login(self): - encryption_client = EncryptionClient() - secret_email = encryption_client.encrypt(self.email) - secret_passwd = encryption_client.encrypt(self.passwd) - secret_dob = encryption_client.encrypt(self.dob) - self.login_payload["body"]["Email_id"] = secret_email - self.login_payload["body"]["Password"] = secret_passwd - self.login_payload["body"]["My2PIN"] = secret_dob - self.login_payload["head"]["requestCode"] = "5PLoginV3" - res = self._login_request(self.LOGIN_ROUTE) - message = res["body"]["Message"] - if message == "": - log_response("Logged in!!") - else: - log_response(message) - self._set_client_code(res["body"]["ClientCode"]) - - def holdings(self): - return self._user_info_request("HOLDINGS") - - def margin(self): - return self._user_info_request("MARGIN") - - def order_book(self): - return self._user_info_request("ORDER_BOOK") - - def positions(self): - return self._user_info_request("POSITIONS") - - def _login_request(self, route): - res = self.session.post(route, json=self.login_payload, headers=HEADERS) - session_cookies = res.cookies - cookies_dictionary = session_cookies.get_dict() - self.Jwt_token=cookies_dictionary['JwtToken'] - - return res.json() - - def _set_client_code(self, client_code): - self.client_code = client_code - - def _user_info_request(self, data_type): - payload = GENERIC_PAYLOAD - payload["body"]["ClientCode"] = self.client_code - return_type = "" - if data_type == "MARGIN": - request_code = self.MARGIN_REQUEST_CODE - url = self.MARGIN_ROUTE - return_type = "EquityMargin" - elif data_type == "ORDER_BOOK": - request_code = self.ORDER_BOOK_REQUEST_CODE - url = self.ORDER_BOOK_ROUTE - return_type = "OrderBookDetail" - elif data_type == "HOLDINGS": - request_code = self.HOLDINGS_REQUEST_CODE - url = self.HOLDINGS_ROUTE - return_type = "Data" - elif data_type == "POSITIONS": - request_code = self.POSITIONS_REQUEST_CODE - url = self.POSITIONS_ROUTE - return_type = "NetPositionDetail" - else: - raise Exception("Invalid data type requested") - - payload["head"]["requestCode"] = request_code - response = self.session.post(url, json=payload, headers=HEADERS).json() - message = response["body"]["Message"] - data = response["body"][return_type] - return data - - def order_request(self, req_type) -> None: - - self.payload["body"]["ClientCode"] = self.client_code - - if req_type == "OP": - url = self.ORDER_PLACEMENT_ROUTE - self.payload["head"]["requestCode"] = "5POrdReq" - elif req_type == "OS": - url = self.ORDER_STATUS_ROUTE - self.payload["head"]["requestCode"] = "5POrdStatus" - elif req_type == "TI": - url = self.TRADE_INFO_ROUTE - self.payload["head"]["requestCode"] = "5PTrdInfo" - elif req_type == "MF": - url = self.MARKET_FEED_ROUTE - self.payload["head"]["requestCode"] = "5PMF" - self.payload["body"]["COUNT"]=self.client_code - elif req_type == "BM": - url = self.BRACKET_MOD_ROUTE - self.payload["head"]["requestCode"] = "5PSModMOOrd" - self.payload["body"]["legtype"]=0 - self.payload["body"]["TMOPartnerOrderID"]=0 - elif req_type == "BO": - url = self.BRACKET_ORDER_ROUTE - self.payload["head"]["requestCode"] = "5PSMOOrd" - self.payload["body"]["OrderRequesterCode"]=self.client_code - else: - raise Exception("Invalid request type!") - - res = self.session.post(url, json=self.payload, - headers=HEADERS).json() - log_response(res["body"]["Message"]) - return res["body"] - - def fetch_order_status(self, req_list:list) : - self.payload["body"]["OrdStatusReqList"] = req_list - return self.order_request("OS") - - def fetch_trade_info(self, req_list:list) : - self.payload["body"]["TradeInformationList"] = req_list - return self.order_request("TI") - - def fetch_market_feed(self, req_list:list) : - """ - market feed api - """ - - self.payload["body"]["MarketFeedData"] = req_list - self.payload["body"]["ClientLoginType"] = 0 - self.payload["body"]["LastRequestTime"] = f"/Date({TODAY_TIMESTAMP})/" - self.payload["body"]["RefreshRate"] = "H" - return self.order_request("MF") - - def set_payload(self, order: Order) -> None: - self.payload["body"]["OrderFor"] = order.order_for - self.payload["body"]["Exchange"] = order.exchange - self.payload["body"]["ExchangeType"] = order.exchange_segment - self.payload["body"]["Price"] = order.price - self.payload["body"]["OrderID"] = order.order_id - self.payload["body"]["OrderType"] = order.order_type - self.payload["body"]["Qty"] = order.quantity - # Passing today's unix timestamp - self.payload["body"]["OrderDateTime"] = f"/Date({TODAY_TIMESTAMP})/" - self.payload["body"]["ScripCode"] = order.scrip_code - self.payload["body"]["AtMarket"] = str(order.atmarket).lower() - self.payload["body"]["RemoteOrderID"] = order.remote_order_id - self.payload["body"]["ExchOrderID"] = order.exch_order_id - self.payload["body"]["DisQty"] = order.disqty - self.payload["body"]["IsStopLossOrder"] = str( - order.is_stoploss_order).lower() - self.payload["body"]["IsVTD"] = str(order.is_vtd).lower() - self.payload["body"]["IOCOrder"] = str(order.ioc_order).lower() - self.payload["body"]["IsIntraday"] = str(order.is_intraday).lower() - self.payload["body"]["PublicIP"] = order.public_ip - self.payload["body"]["AHPlaced"] = order.ahplaced - # Passing the next day's UNIX timestamp - self.payload["body"]["ValidTillDate"] = f"/Date({NEXT_DAY_TIMESTAMP})/" - self.payload["body"]["TradedQty"] = order.traded_qty - self.payload["body"]["OrderRequesterCode"] = self.client_code - self.payload["body"]["AppSource"] = APP_SOURCE - self.payload["body"]["iOrderValidity"] = order.order_validity - - def set_payload_bo(self,boco:bo_co_order)-> None: - """ - this is for bo-co order placement - """ - self.payload["body"]["RequestType"] = boco.RequestType - self.payload["body"]["BuySell"] = boco.BuySell - self.payload["body"]["Qty"] = boco.Qty - self.payload["body"]["Exch"] = boco.Exch - self.payload["body"]["ExchType"] = boco.ExchType - self.payload["body"]["DisQty"] = boco.DisQty - self.payload["body"]["AtMarket"] = boco.AtMarket - self.payload["body"]["ExchOrderId"] = boco.ExchOrderId - self.payload["body"]["LimitPriceInitialOrder"] = boco.LimitPriceInitialOrder - self.payload["body"]["TriggerPriceInitialOrder"] = boco.TriggerPriceInitialOrder - self.payload["body"]["LimitPriceProfitOrder"] = boco.LimitPriceProfitOrder - self.payload["body"]["TriggerPriceForSL"] = boco.TriggerPriceForSL - self.payload["body"]["TrailingSL"] = boco.TrailingSL - self.payload["body"]["StopLoss"] = boco.StopLoss - self.payload["body"]["ScripCode"] = boco.scrip_code - self.payload["body"]["OrderFor"] = boco.order_for - self.payload["body"]["UniqueOrderIDNormal"] = boco.UniqueOrderIDNormal - self.payload["body"]["UniqueOrderIDSL"] = boco.UniqueOrderIDSL - self.payload["body"]["UniqueOrderIDLimit"] = boco.UniqueOrderIDLimit - self.payload["body"]["LocalOrderIDNormal"] = boco.LocalOrderIDNormal - self.payload["body"]["LocalOrderIDSL"] = boco.LocalOrderIDSL - self.payload["body"]["LocalOrderIDLimit"] = boco.LocalOrderIDLimit - self.payload["body"]["PublicIP"] = boco.public_ip - self.payload["body"]["AppSource"] = boco.app_source - self.payload["body"]["TradedQty"] = boco.traded_qty - - def place_order(self, order: Order): - """ - Places a fresh order - """ - self.set_payload(order) - self.payload["body"]["StopLossPrice"] = order.stoploss_price - return self.order_request("OP") - - def modify_order(self, order: Order): - """ - Modifies an existing order - """ - - self.set_payload(order) - self.payload["body"]["StopLossPrice"] = order.stoploss_price - self.payload["body"]["OrderFor"] = "M" - - return self.order_request("OP") - - def cancel_order(self,order_type:str, scrip_code:int, quantity:int,exchange:str,exchange_segment:str,exch_order_id:str): - """ - Cancels an existing order - """ - order = Order(order_type=order_type, scrip_code=scrip_code, - quantity=quantity,exchange=exchange,exchange_segment=exchange_segment, exch_order_id=exch_order_id, price=0.0,atmarket=False,is_intraday=False,order_for='C') - self.set_payload(order) - self.payload["body"]["StopLossPrice"] = order.stoploss_price - return self.order_request("OP") - - def bo_order(self,boco:bo_co_order): - self.set_payload_bo(boco) - return self.order_request("BO") - - def mod_bo_order(self,order: Order): - self.set_payload(order) - self.payload["body"]["TriggerPriceForSL"] = order.stoploss_price - return self.order_request("BM") - - def Request_Feed(self,Method:str,Operation:str,req_list:list): - Method_dict={"mf":"MarketFeedV3","md":"MarketDepthService","oi":"GetScripInfoForFuture"} - Operation_dict={"s":"Subscribe","u":"Unsubscribe"} - - self.ws_payload['Method']=Method_dict[Method] - self.ws_payload['Operation']=Operation_dict[Operation] - self.ws_payload['ClientCode']=self.client_code - self.ws_payload['MarketFeedData']=req_list - return self.ws_payload - def Streming_data(self,wsPayload : dict): - self.web_url=f'wss://openfeed.5paisa.com/Feeds/api/chat?Value1={self.Jwt_token}|{self.client_code}' - auth=self.Login_check() - - def on_message(ws, message): - print(message) - - def on_error(ws, error): - print(error) - - def on_close(ws): - print("Streaming Stopped") - - def on_open(ws): - print("Streaming Started") - ws.send(json.dumps(wsPayload)) - - - ws = websocket.WebSocketApp(self.web_url, - on_open=on_open, - on_message = on_message, - on_error = on_error, - on_close = on_close, - cookie=auth) - - ws.run_forever() - - def Login_check(self): - self.login_check_payload["head"]["LoginId"]=self.client_code - self.login_check_payload["body"]["RegistrationID"]=self.Jwt_token - url=self.LOGIN_CHECK_ROUTE - resl=requests.post(url, json=self.login_check_payload,headers=HEADERS) - self.Aspx_auth = resl.cookies.get('.ASPXAUTH',domain='openfeed.5paisa.com') - - return f'.ASPXAUTH={self.Aspx_auth}' - - - - From 76148d7a8343a2f519f4144ef23b9b26a4d92b0d Mon Sep 17 00:00:00 2001 From: 5paisa <65898074+5paisa@users.noreply.github.com> Date: Fri, 18 Jun 2021 12:04:16 +0530 Subject: [PATCH 065/335] Add files via upload --- py5paisa/py5paisa.py | 379 +++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 379 insertions(+) create mode 100644 py5paisa/py5paisa.py diff --git a/py5paisa/py5paisa.py b/py5paisa/py5paisa.py new file mode 100644 index 00000000..90e66f1b --- /dev/null +++ b/py5paisa/py5paisa.py @@ -0,0 +1,379 @@ +import requests +from requests.packages.urllib3.exceptions import InsecureRequestWarning +from .auth import EncryptionClient +from .const import GENERIC_PAYLOAD, LOGIN_PAYLOAD, HEADERS, NEXT_DAY_TIMESTAMP, TODAY_TIMESTAMP,LOGIN_CHECK_PAYLOAD,WS_PAYLOAD,JWT_PAYLOAD,JWT_HEADERS +from .conf import APP_SOURCE +from .order import Order, bo_co_order, OrderType, OrderFor +from .logging import log_response +import datetime +from typing import Union +import json +import websocket +import pandas as pd + +class FivePaisaClient: + + LOGIN_ROUTE = "https://Openapi.5paisa.com/VendorsAPI/Service1.svc/V3/LoginRequestMobileNewbyEmail" + + MARGIN_ROUTE = "https://Openapi.5paisa.com/VendorsAPI/Service1.svc/V3/Margin" + ORDER_BOOK_ROUTE = "https://Openapi.5paisa.com/VendorsAPI/Service1.svc/V2/OrderBook" + HOLDINGS_ROUTE = "https://openapi.5paisa.com/VendorsAPI/Service1.svc/V2/Holding" + POSITIONS_ROUTE = "https://Openapi.5paisa.com/VendorsAPI/Service1.svc/V1/NetPositionNetWise" + + ORDER_PLACEMENT_ROUTE = "https://Openapi.5paisa.com/VendorsAPI/Service1.svc/V1/OrderRequest" + ORDER_STATUS_ROUTE = "https://Openapi.5paisa.com/VendorsAPI/Service1.svc/OrderStatus" + TRADE_INFO_ROUTE = "https://Openapi.5paisa.com/VendorsAPI/Service1.svc/TradeInformation" + + BRACKET_MOD_ROUTE="https://openapi.5paisa.com/VendorsAPI/Service1.svc/ModifySMOOrder" + BRACKET_ORDER_ROUTE="https://openapi.5paisa.com/VendorsAPI/Service1.svc/SMOOrderRequest" + + MARKET_FEED_ROUTE="https://Openapi.5paisa.com/VendorsAPI/Service1.svc/MarketFeed" + LOGIN_CHECK_ROUTE="https://openfeed.5paisa.com/Feeds/api/UserActivity/LoginCheck" + + MARKET_DEPTH_ROUTE="https://openapi.5paisa.com/VendorsAPI/Service1.svc/MarketDepth" + JWT_VALIDATION_ROUTE="https://Openapi.indiainfoline.com/VendorsAPI/Service1.svc/JWTOpenApiValidation" + HISTORICAL_DATA_ROUTE="https://openapi.5paisa.com/historical/" + + MARGIN_REQUEST_CODE = "5PMarginV3" + ORDER_BOOK_REQUEST_CODE = "5POrdBkV2" + HOLDINGS_REQUEST_CODE = "5PHoldingV2" + POSITIONS_REQUEST_CODE = "5PNPNWV1" + + def __init__(self, email=None, passwd=None, dob=None): + """ + Main constructor for client. + Expects user's email, password and date of birth in YYYYMMDD format. + """ + self.email = email + self.passwd = passwd + self.dob = dob + self.payload = GENERIC_PAYLOAD + self.login_payload = LOGIN_PAYLOAD + self.login_check_payload= LOGIN_CHECK_PAYLOAD + self.ws_payload=WS_PAYLOAD + self.jwt_headers=JWT_HEADERS + self.jwt_payload=JWT_PAYLOAD + self.client_code = None + self.Jwt_token = None + self.Aspx_auth = None + self.web_url= None + self.session = requests.Session() + + def login(self): + encryption_client = EncryptionClient() + secret_email = encryption_client.encrypt(self.email) + secret_passwd = encryption_client.encrypt(self.passwd) + secret_dob = encryption_client.encrypt(self.dob) + self.login_payload["body"]["Email_id"] = secret_email + self.login_payload["body"]["Password"] = secret_passwd + self.login_payload["body"]["My2PIN"] = secret_dob + self.login_payload["head"]["requestCode"] = "5PLoginV3" + res = self._login_request(self.LOGIN_ROUTE) + message = res["body"]["Message"] + if message == "": + log_response("Logged in!!") + else: + log_response(message) + self._set_client_code(res["body"]["ClientCode"]) + + def holdings(self): + return self._user_info_request("HOLDINGS") + + def margin(self): + return self._user_info_request("MARGIN") + + def order_book(self): + return self._user_info_request("ORDER_BOOK") + + def positions(self): + return self._user_info_request("POSITIONS") + + def _login_request(self, route): + requests.packages.urllib3.disable_warnings(InsecureRequestWarning) + res = self.session.post(route, json=self.login_payload, headers=HEADERS) + + session_cookies = res.cookies + + cookies_dictionary = session_cookies.get_dict() + self.Jwt_token=cookies_dictionary['JwtToken'] + + + return res.json() + + def _set_client_code(self, client_code): + self.client_code = client_code + + def _user_info_request(self, data_type): + payload = GENERIC_PAYLOAD + payload["body"]["ClientCode"] = self.client_code + return_type = "" + if data_type == "MARGIN": + request_code = self.MARGIN_REQUEST_CODE + url = self.MARGIN_ROUTE + return_type = "EquityMargin" + elif data_type == "ORDER_BOOK": + request_code = self.ORDER_BOOK_REQUEST_CODE + url = self.ORDER_BOOK_ROUTE + return_type = "OrderBookDetail" + elif data_type == "HOLDINGS": + request_code = self.HOLDINGS_REQUEST_CODE + url = self.HOLDINGS_ROUTE + return_type = "Data" + elif data_type == "POSITIONS": + request_code = self.POSITIONS_REQUEST_CODE + url = self.POSITIONS_ROUTE + return_type = "NetPositionDetail" + else: + raise Exception("Invalid data type requested") + + payload["head"]["requestCode"] = request_code + response = self.session.post(url, json=payload, headers=HEADERS).json() + message = response["body"]["Message"] + data = response["body"][return_type] + return data + + def order_request(self, req_type) -> None: + + self.payload["body"]["ClientCode"] = self.client_code + + if req_type == "OP": + url = self.ORDER_PLACEMENT_ROUTE + self.payload["head"]["requestCode"] = "5POrdReq" + elif req_type == "OS": + url = self.ORDER_STATUS_ROUTE + self.payload["head"]["requestCode"] = "5POrdStatus" + elif req_type == "TI": + url = self.TRADE_INFO_ROUTE + self.payload["head"]["requestCode"] = "5PTrdInfo" + elif req_type == "MF": + url = self.MARKET_FEED_ROUTE + self.payload["head"]["requestCode"] = "5PMF" + self.payload["body"]["COUNT"]=self.client_code + elif req_type == "BM": + url = self.BRACKET_MOD_ROUTE + self.payload["head"]["requestCode"] = "5PSModMOOrd" + self.payload["body"]["legtype"]=0 + self.payload["body"]["TMOPartnerOrderID"]=0 + elif req_type == "BO": + url = self.BRACKET_ORDER_ROUTE + self.payload["head"]["requestCode"] = "5PSMOOrd" + self.payload["body"]["OrderRequesterCode"]=self.client_code + elif req_type == "MD": + url = self.MARKET_DEPTH_ROUTE + self.payload["head"]["requestCode"] = "5PMD" + + else: + raise Exception("Invalid request type!") + + + res = self.session.post(url, json=self.payload, + headers=HEADERS).json() + log_response(res["body"]["Message"]) + return res["body"] + + def fetch_order_status(self, req_list:list) : + self.payload["body"]["OrdStatusReqList"] = req_list + return self.order_request("OS") + + def fetch_trade_info(self, req_list:list) : + self.payload["body"]["TradeInformationList"] = req_list + return self.order_request("TI") + + def fetch_market_depth(self, req_list:list): + self.payload["body"]["Count"]="1" + self.payload["body"]["Data"]=req_list + + return self.order_request("MD") + + + def fetch_market_feed(self, req_list:list) : + """ + market feed api + """ + + self.payload["body"]["MarketFeedData"] = req_list + self.payload["body"]["ClientLoginType"] = 0 + self.payload["body"]["LastRequestTime"] = f"/Date({TODAY_TIMESTAMP})/" + self.payload["body"]["RefreshRate"] = "H" + return self.order_request("MF") + + def set_payload(self, order: Order) -> None: + self.payload["body"]["OrderFor"] = order.order_for + self.payload["body"]["Exchange"] = order.exchange + self.payload["body"]["ExchangeType"] = order.exchange_segment + self.payload["body"]["Price"] = order.price + self.payload["body"]["OrderID"] = order.order_id + self.payload["body"]["OrderType"] = order.order_type + self.payload["body"]["Qty"] = order.quantity + # Passing today's unix timestamp + self.payload["body"]["OrderDateTime"] = f"/Date({TODAY_TIMESTAMP})/" + self.payload["body"]["ScripCode"] = order.scrip_code + self.payload["body"]["AtMarket"] = str(order.atmarket).lower() + self.payload["body"]["RemoteOrderID"] = order.remote_order_id + self.payload["body"]["ExchOrderID"] = order.exch_order_id + self.payload["body"]["DisQty"] = order.disqty + self.payload["body"]["IsStopLossOrder"] = str( + order.is_stoploss_order).lower() + self.payload["body"]["IsVTD"] = str(order.is_vtd).lower() + self.payload["body"]["IOCOrder"] = str(order.ioc_order).lower() + self.payload["body"]["IsIntraday"] = str(order.is_intraday).lower() + self.payload["body"]["PublicIP"] = order.public_ip + self.payload["body"]["AHPlaced"] = order.ahplaced + # Passing the next day's UNIX timestamp + self.payload["body"]["ValidTillDate"] = f"/Date({NEXT_DAY_TIMESTAMP})/" + self.payload["body"]["TradedQty"] = order.traded_qty + self.payload["body"]["OrderRequesterCode"] = self.client_code + self.payload["body"]["AppSource"] = APP_SOURCE + self.payload["body"]["iOrderValidity"] = order.order_validity + + def set_payload_bo(self,boco:bo_co_order)-> None: + """ + this is for bo-co order placement + """ + self.payload["body"]["RequestType"] = boco.RequestType + self.payload["body"]["BuySell"] = boco.BuySell + self.payload["body"]["Qty"] = boco.Qty + self.payload["body"]["Exch"] = boco.Exch + self.payload["body"]["ExchType"] = boco.ExchType + self.payload["body"]["DisQty"] = boco.DisQty + self.payload["body"]["AtMarket"] = boco.AtMarket + self.payload["body"]["ExchOrderId"] = boco.ExchOrderId + self.payload["body"]["LimitPriceInitialOrder"] = boco.LimitPriceInitialOrder + self.payload["body"]["TriggerPriceInitialOrder"] = boco.TriggerPriceInitialOrder + self.payload["body"]["LimitPriceProfitOrder"] = boco.LimitPriceProfitOrder + self.payload["body"]["TriggerPriceForSL"] = boco.TriggerPriceForSL + self.payload["body"]["TrailingSL"] = boco.TrailingSL + self.payload["body"]["StopLoss"] = boco.StopLoss + self.payload["body"]["ScripCode"] = boco.scrip_code + self.payload["body"]["OrderFor"] = boco.order_for + self.payload["body"]["UniqueOrderIDNormal"] = boco.UniqueOrderIDNormal + self.payload["body"]["UniqueOrderIDSL"] = boco.UniqueOrderIDSL + self.payload["body"]["UniqueOrderIDLimit"] = boco.UniqueOrderIDLimit + self.payload["body"]["LocalOrderIDNormal"] = boco.LocalOrderIDNormal + self.payload["body"]["LocalOrderIDSL"] = boco.LocalOrderIDSL + self.payload["body"]["LocalOrderIDLimit"] = boco.LocalOrderIDLimit + self.payload["body"]["PublicIP"] = boco.public_ip + self.payload["body"]["AppSource"] = boco.app_source + self.payload["body"]["TradedQty"] = boco.traded_qty + + def place_order(self, order: Order): + """ + Places a fresh order + """ + self.set_payload(order) + self.payload["body"]["StopLossPrice"] = order.stoploss_price + return self.order_request("OP") + + def modify_order(self, order: Order): + """ + Modifies an existing order + """ + + self.set_payload(order) + self.payload["body"]["StopLossPrice"] = order.stoploss_price + self.payload["body"]["OrderFor"] = "M" + + return self.order_request("OP") + + def cancel_order(self,order_type:str, scrip_code:int, quantity:int,exchange:str,exchange_segment:str,exch_order_id:str): + """ + Cancels an existing order + """ + order = Order(order_type=order_type, scrip_code=scrip_code, + quantity=quantity,exchange=exchange,exchange_segment=exchange_segment, exch_order_id=exch_order_id, price=0.0,atmarket=False,is_intraday=False,order_for='C') + self.set_payload(order) + self.payload["body"]["StopLossPrice"] = order.stoploss_price + return self.order_request("OP") + + def bo_order(self,boco:bo_co_order): + self.set_payload_bo(boco) + return self.order_request("BO") + + def mod_bo_order(self,order: Order): + self.set_payload(order) + self.payload["body"]["TriggerPriceForSL"] = order.stoploss_price + return self.order_request("BM") + + def Request_Feed(self,Method:str,Operation:str,req_list:list): + Method_dict={"mf":"MarketFeedV3","md":"MarketDepthService","oi":"GetScripInfoForFuture"} + Operation_dict={"s":"Subscribe","u":"Unsubscribe"} + + self.ws_payload['Method']=Method_dict[Method] + self.ws_payload['Operation']=Operation_dict[Operation] + self.ws_payload['ClientCode']=self.client_code + self.ws_payload['MarketFeedData']=req_list + return self.ws_payload + def Streming_data(self,wsPayload : dict): + self.web_url=f'wss://openfeed.5paisa.com/Feeds/api/chat?Value1={self.Jwt_token}|{self.client_code}' + auth=self.Login_check() + + def on_message(ws, message): + print(message) + + def on_error(ws, error): + print(error) + + def on_close(ws): + print("Streaming Stopped") + + def on_open(ws): + print("Streaming Started") + ws.send(json.dumps(wsPayload)) + + + ws = websocket.WebSocketApp(self.web_url, + on_open=on_open, + on_message = on_message, + on_error = on_error, + on_close = on_close, + cookie=auth) + + ws.run_forever() + + def Login_check(self): + self.login_check_payload["head"]["LoginId"]=self.client_code + self.login_check_payload["body"]["RegistrationID"]=self.Jwt_token + url=self.LOGIN_CHECK_ROUTE + resl=requests.post(url, json=self.login_check_payload,headers=HEADERS) + self.Aspx_auth = resl.cookies.get('.ASPXAUTH',domain='openfeed.5paisa.com') + + return f'.ASPXAUTH={self.Aspx_auth}' + + def jwt_validate(self): + self.jwt_payload['ClientCode']=self.client_code + self.jwt_payload['JwtCode']=self.Jwt_token + url=self.JWT_VALIDATION_ROUTE + response = self.session.post(url, json=self.jwt_payload, headers=HEADERS).json() + + return response['body']['Message'] + + def historical_data(self,Exch:str,ExchangeSegment:str,ScripCode: int,time: str,From:str,To: str): + validation=self.jwt_validate() + + + if validation=='Authorization Successful': + self.jwt_headers['x-clientcode']=self.client_code + self.jwt_headers['x-auth-token']=self.Jwt_token + url=f'{self.HISTORICAL_DATA_ROUTE}{Exch}/{ExchangeSegment}/{ScripCode}/{time}?from={From}&end={To}' + timeList=['1m','5m','10m','15m','30m','60m','1d'] + if time not in timeList: + return 'Invalid Time Frame. it should be within [1m,5m,10m,15m,30m,60m,1d].' + else: + + + response = self.session.get(url, headers=self.jwt_headers).json() + candleList=response['data']['candles'] + + df=pd.DataFrame(candleList) + + df.columns=['Datetime','Open','High','Low','Close','Volume'] + + return df + + else: + return 'Invalid JWT.' + + + + + From 95ff4070e608285628c35c3dadc409271a6d6fb9 Mon Sep 17 00:00:00 2001 From: 5paisa <65898074+5paisa@users.noreply.github.com> Date: Fri, 18 Jun 2021 12:05:05 +0530 Subject: [PATCH 066/335] Delete const.py --- py5paisa/const.py | 69 ----------------------------------------------- 1 file changed, 69 deletions(-) delete mode 100644 py5paisa/const.py diff --git a/py5paisa/const.py b/py5paisa/const.py deleted file mode 100644 index 78c67fe3..00000000 --- a/py5paisa/const.py +++ /dev/null @@ -1,69 +0,0 @@ -""" -Contains reusable payloads across requests -""" -from .conf import APP_NAME, USER_ID, PASSWORD, USER_KEY -import datetime - -HEADERS = {'Content-Type': 'application/json'} - -GENERIC_PAYLOAD = { - "head": { - "appName": APP_NAME, - "appVer": "1.0", - "key": USER_KEY, - "osName": "WEB", - "requestCode": "", - "userId": USER_ID, - "password": PASSWORD - }, - "body": { - "ClientCode": "" - } -} - -LOGIN_PAYLOAD = {"head": { - "appName": APP_NAME, - "appVer": "1.0", - "key": USER_KEY, - "osName": "WEB", - "requestCode": "5PLoginV2", - "userId": USER_ID, - "password": PASSWORD -}, - "body": - { - "Email_id": "", - "Password": "", - "LocalIP": "192.168.10.10", - "PublicIP": "192.168.10.10", - "HDSerailNumber": "", - "MACAddress": "", - "MachineID": "039377", - "VersionNo": "1.7", - "RequestNo": "1", - "My2PIN": "", - "ConnectionType": "1" -} -} -LOGIN_CHECK_PAYLOAD={ - "head" : { - "requestCode":"5PLoginCheck", - "key":USER_KEY, - "appVer":"1.0", - "appName":APP_NAME, - "osName":"WEB", - "LoginId":"" - }, - "body":{ - "RegistrationID":"" - } - } - -WS_PAYLOAD={"Method":"", - "Operation":"", - "ClientCode":"", - "MarketFeedData":""} - -TODAY_TIMESTAMP = int(datetime.datetime.today().timestamp()) -NEXT_DAY_TIMESTAMP = int( - (datetime.datetime.today()+datetime.timedelta(days=1)).timestamp()) From c54cc1fa80e1ef7939b6c181e8ac38f559aea4b6 Mon Sep 17 00:00:00 2001 From: 5paisa <65898074+5paisa@users.noreply.github.com> Date: Fri, 18 Jun 2021 12:05:26 +0530 Subject: [PATCH 067/335] Add files via upload --- py5paisa/const.py | 80 +++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 80 insertions(+) create mode 100644 py5paisa/const.py diff --git a/py5paisa/const.py b/py5paisa/const.py new file mode 100644 index 00000000..5686f44d --- /dev/null +++ b/py5paisa/const.py @@ -0,0 +1,80 @@ +""" +Contains reusable payloads across requests +""" +from .conf import APP_NAME, USER_ID, PASSWORD, USER_KEY +import datetime + +HEADERS = {'Content-Type': 'application/json'} + +GENERIC_PAYLOAD = { + "head": { + "appName": APP_NAME, + "appVer": "1.0", + "key": USER_KEY, + "osName": "WEB", + "requestCode": "", + "userId": USER_ID, + "password": PASSWORD + }, + "body": { + "ClientCode": "" + } +} + +LOGIN_PAYLOAD = {"head": { + "appName": APP_NAME, + "appVer": "1.0", + "key": USER_KEY, + "osName": "WEB", + "requestCode": "5PLoginV2", + "userId": USER_ID, + "password": PASSWORD +}, + "body": + { + "Email_id": "", + "Password": "", + "LocalIP": "192.168.10.10", + "PublicIP": "192.168.10.10", + "HDSerailNumber": "", + "MACAddress": "", + "MachineID": "039377", + "VersionNo": "1.7", + "RequestNo": "1", + "My2PIN": "", + "ConnectionType": "1" +} +} +LOGIN_CHECK_PAYLOAD={ + "head" : { + "requestCode":"5PLoginCheck", + "key":USER_KEY, + "appVer":"1.0", + "appName":APP_NAME, + "osName":"WEB", + "LoginId":"" + }, + "body":{ + "RegistrationID":"" + } + } + +WS_PAYLOAD={"Method":"", + "Operation":"", + "ClientCode":"", + "MarketFeedData":""} + +JWT_HEADERS={ + 'Ocp-Apim-Subscription-Key': 'c89fab8d895a426d9e00db380b433027', + 'x-clientcode':"", + 'x-auth-token':"" + } + +JWT_PAYLOAD={ + "ClientCode":"", + "JWTToken":"" + } + +TODAY_TIMESTAMP = int(datetime.datetime.today().timestamp()) +NEXT_DAY_TIMESTAMP = int( + (datetime.datetime.today()+datetime.timedelta(days=1)).timestamp()) From 650cb20062d1b3e9dfbdc048192a4a82ea8cc6b1 Mon Sep 17 00:00:00 2001 From: 5paisa <65898074+5paisa@users.noreply.github.com> Date: Fri, 18 Jun 2021 12:08:18 +0530 Subject: [PATCH 068/335] Update setup.py --- setup.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/setup.py b/setup.py index c5c5896e..231c237a 100644 --- a/setup.py +++ b/setup.py @@ -19,6 +19,7 @@ "urllib3>=1.25.8", "idna>=2.9", "loguru>=0.5.1", + "websocket-client" ] setup_requirements = [] @@ -47,6 +48,6 @@ test_suite='tests', tests_require=test_requirements, url='https://github.com/5paisa/py5paisa', - version='0.2.7', + version='0.2.8', zip_safe=False, ) From fdd5a40a89c3df81f96f6aee689630db4464cd53 Mon Sep 17 00:00:00 2001 From: 5paisa <65898074+5paisa@users.noreply.github.com> Date: Fri, 18 Jun 2021 12:09:58 +0530 Subject: [PATCH 069/335] Update setup.py --- setup.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/setup.py b/setup.py index 231c237a..552536d2 100644 --- a/setup.py +++ b/setup.py @@ -19,7 +19,8 @@ "urllib3>=1.25.8", "idna>=2.9", "loguru>=0.5.1", - "websocket-client" + "websocket-client", + "pandas", ] setup_requirements = [] From 789ec4c8156e787ae90f563390270e54583a82f8 Mon Sep 17 00:00:00 2001 From: 5paisa <65898074+5paisa@users.noreply.github.com> Date: Fri, 18 Jun 2021 12:13:41 +0530 Subject: [PATCH 070/335] Update README.md --- README.md | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/README.md b/README.md index 2b833d3d..3cd66636 100644 --- a/README.md +++ b/README.md @@ -212,6 +212,21 @@ Subscribe= s Unsubscribe=u +#### Market Depth +```py +a=[{"Exchange":"N","ExchangeType":"C","ScripCode":"2885"}, + {"Exchange":"N","ExchangeType":"C","ScripCode":"1660"}, + ] +print(Client.fetch_market_depth(a)) +``` +#### Historical Data +```py +df=Client.historical_data('N','C',1660,'1d','2021-05-25','2021-06-16') +print(df) + +# Note : TimeFrame Should be from this list ['1m','5m','10m','15m','30m','60m','1d'] +``` + #### Strategy Execution #### List Of Strategies Available - Short Straddle From 628ec4c5addc026955857548dcc01a1445d62bc9 Mon Sep 17 00:00:00 2001 From: 5paisa <65898074+5paisa@users.noreply.github.com> Date: Fri, 18 Jun 2021 16:49:08 +0530 Subject: [PATCH 071/335] Update setup.py --- setup.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/setup.py b/setup.py index 552536d2..755c151d 100644 --- a/setup.py +++ b/setup.py @@ -19,8 +19,8 @@ "urllib3>=1.25.8", "idna>=2.9", "loguru>=0.5.1", - "websocket-client", - "pandas", + "websocket-client>=0.58.0", + "pandas>=1.2.4", ] setup_requirements = [] From bcb46f85809bdbbded649c80244919b7c0c940c6 Mon Sep 17 00:00:00 2001 From: 5paisa <65898074+5paisa@users.noreply.github.com> Date: Fri, 18 Jun 2021 16:52:53 +0530 Subject: [PATCH 072/335] Update README.md --- README.md | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index 3cd66636..0cc33f12 100644 --- a/README.md +++ b/README.md @@ -221,7 +221,9 @@ print(Client.fetch_market_depth(a)) ``` #### Historical Data ```py -df=Client.historical_data('N','C',1660,'1d','2021-05-25','2021-06-16') +#historical_data(,,,