├── LICENSE ├── README.md ├── access_token.py ├── requirements.txt ├── strategy_PSAR.py └── utils.py /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2021 Pramay 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Python-Algo-Trading-Zerodha 2 | This repo contains sample templates for implementing strategies in python. 3 | 4 | I have used Zerodha's kiteconnect api for developing sample strategies. 5 | 6 | ## Zerodha kiteconnect 7 | Here are some important links that you may need to go through to understand the login and other related stuff. 8 | 9 | 1. [KiteConnect API Doc](https://kite.trade/docs/connect/v3/) 10 | 2. [KiteConnect Python Doc](https://kite.trade/docs/pykiteconnect/v3/) 11 | 3. [KiteConnect github page](https://github.com/zerodhatech/pykiteconnect) 12 | 4. [KiteConnect developer login](https://developers.kite.trade/login) 13 | 5. [KiteConnect developer forum](https://kite.trade/forum/) 14 | 15 | ## Install dependencies 16 | 17 | ``` pip install -r requirements.txt ``` 18 | 19 | ## Automate the login process 20 | 21 | ### For first time run 22 | 23 | 1. ```pip install selenium``` 24 | 2. Download the correct webdriver from [here](https://pypi.org/project/selenium/) and place the .exe file in the directory having ```access_token.py``` 25 | 3. Create api_key.txt file with the following info - 26 | * API KEY - Obtained from the trading app in your developer account. 27 | * API SECRET - Obtained from the trading app in your developer account. 28 | * USER ID - Zerodha Kite user id. 29 | * PASSWORD - Zerodha Kite password. 30 | * PIN - Zerodha Kite pin. 31 | 32 | #### ```python access_token.py``` 33 | 34 | ## Strategy 35 | Buy when PSAR crosses below candle and sell when PSAR crosses above candle. 36 | The code for the same is in ```strategy_PSAR.py``` and other utility functions are in ```utils.py``` 37 | 38 | ## Daily Usage 39 | 40 | It is recommended to change the global variables in ```strategy_PSAR.py``` as per the new startegy. Also run the ```strategy_PSAR.py``` a minute later than candle start( if working with larger duration candles like 15minutes or bigger). For ex. if you want to start at 9:15 AM run the code at 9:16 AM to avoid some signals being missed. 41 | 42 | 1. run ```python access_token.py``` 43 | 2. run ```strategy_PSAR.py``` 44 | 45 | ## NOTE_1 - This is just a sample strategy to get started with kiteconnect and zerodha. It is always recommended to use one's own strategy for best results. 46 | ## NOTE_2 - For coding technical indicators use [pandas-ta](https://github.com/twopirllc/pandas-ta) and NOT [TA-Lib](https://www.google.com/url?sa=t&rct=j&q=&esrc=s&source=web&cd=&cad=rja&uact=8&ved=2ahUKEwjrjfvJo6PvAhWO4XMBHUdlAf0QFjAAegQIAhAD&url=https%3A%2F%2Fpypi.org%2Fproject%2FTA-Lib%2F&usg=AOvVaw2pqi0Y5nrF2nJuDzzPky1D). TA-Lib gave wrong PSAR values many times during PSAR reversals. -------------------------------------------------------------------------------- /access_token.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | 3 | from kiteconnect import KiteConnect 4 | from selenium import webdriver 5 | import time 6 | import os 7 | 8 | 9 | cwd = os.getcwd() 10 | 11 | 12 | def autologin(): 13 | token_path = "api_key.txt" 14 | key_secret = open(token_path, 'r').read().split() 15 | kite = KiteConnect(api_key=key_secret[0]) 16 | service = webdriver.chrome.service.Service('./chromedriver') 17 | service.start() 18 | options = webdriver.ChromeOptions() 19 | options.add_argument('--headless') 20 | options = options.to_capabilities() 21 | driver = webdriver.Remote(service.service_url, options) 22 | driver.get(kite.login_url()) 23 | driver.implicitly_wait(10) 24 | username = driver.find_element_by_xpath( 25 | '/html/body/div[1]/div/div[2]/div[1]/div/div/div[2]/form/div[1]/input') 26 | password = driver.find_element_by_xpath( 27 | '/html/body/div[1]/div/div[2]/div[1]/div/div/div[2]/form/div[2]/input') 28 | username.send_keys(key_secret[2]) 29 | password.send_keys(key_secret[3]) 30 | driver.find_element_by_xpath( 31 | '/html/body/div[1]/div/div[2]/div[1]/div/div/div[2]/form/div[4]/button').click() 32 | pin = driver.find_element_by_xpath( 33 | '/html/body/div[1]/div/div[2]/div[1]/div/div/div[2]/form/div[2]/div/input') 34 | pin.send_keys(key_secret[4]) 35 | driver.find_element_by_xpath( 36 | '/html/body/div[1]/div/div[2]/div[1]/div/div/div[2]/form/div[3]/button').click() 37 | time.sleep(10) 38 | request_token = driver.current_url.split('=')[1].split('&action')[0] 39 | with open('request_token.txt', 'w') as the_file: 40 | the_file.write(request_token) 41 | driver.quit() 42 | 43 | 44 | autologin() 45 | 46 | # generating and storing access token - valid till 6 am the next day 47 | request_token = open("request_token.txt", 'r').read() 48 | key_secret = open("api_key.txt", 'r').read().split() 49 | kite = KiteConnect(api_key=key_secret[0]) 50 | data = kite.generate_session(request_token, api_secret=key_secret[1]) 51 | with open('access_token.txt', 'w') as file: 52 | file.write(data["access_token"]) 53 | -------------------------------------------------------------------------------- /requirements.txt: -------------------------------------------------------------------------------- 1 | alabaster @ file:///home/ktietz/src/ci/alabaster_1611921544520/work 2 | anyio==2.1.0 3 | appdirs==1.4.4 4 | argh==0.26.2 5 | argon2-cffi @ file:///C:/ci/argon2-cffi_1613037959010/work 6 | astroid @ file:///C:/ci/astroid_1592487315634/work 7 | async-generator @ file:///home/ktietz/src/ci/async_generator_1611927993394/work 8 | atomicwrites==1.4.0 9 | attrs @ file:///tmp/build/80754af9/attrs_1604765588209/work 10 | autobahn==19.11.2 11 | Automat==20.2.0 12 | autopep8 @ file:///tmp/build/80754af9/autopep8_1613246362269/work 13 | Babel @ file:///tmp/build/80754af9/babel_1607110387436/work 14 | backcall @ file:///home/ktietz/src/ci/backcall_1611930011877/work 15 | bcrypt @ file:///C:/ci/bcrypt_1597936263757/work 16 | beautifulsoup4==4.9.3 17 | black==19.10b0 18 | bleach @ file:///tmp/build/80754af9/bleach_1612211392645/work 19 | brotlipy==0.7.0 20 | bs4==0.0.1 21 | certifi==2020.6.20 22 | cffi @ file:///C:/ci/cffi_1613247279197/work 23 | chardet @ file:///C:/ci/chardet_1607690654534/work 24 | click @ file:///home/linux1/recipes/ci/click_1610990599742/work 25 | cloudpickle @ file:///tmp/build/80754af9/cloudpickle_1598884132938/work 26 | colorama @ file:///tmp/build/80754af9/colorama_1607707115595/work 27 | constantly==15.1.0 28 | cryptography @ file:///C:/ci/cryptography_1607637849569/work 29 | cycler==0.10.0 30 | decorator @ file:///home/ktietz/src/ci/decorator_1611930055503/work 31 | defusedxml @ file:///home/ktietz/src/ci_mi/defusedxml_1612808095953/work 32 | diff-match-patch @ file:///tmp/build/80754af9/diff-match-patch_1594828741838/work 33 | docutils==0.16 34 | easydict==1.9 35 | entrypoints==0.3 36 | enum34==1.1.10 37 | et-xmlfile==1.0.1 38 | flake8 @ file:///tmp/build/80754af9/flake8_1601911421857/work 39 | Flask==1.1.1 40 | future==0.18.2 41 | hyperlink==21.0.0 42 | idna @ file:///home/linux1/recipes/ci/idna_1610986105248/work 43 | imagesize @ file:///home/ktietz/src/ci/imagesize_1611921604382/work 44 | importlib-metadata @ file:///tmp/build/80754af9/importlib-metadata_1602276842396/work 45 | incremental==17.5.0 46 | intervaltree @ file:///tmp/build/80754af9/intervaltree_1598376443606/work 47 | ipykernel==5.4.3 48 | ipython @ file:///D:/bld/ipython_1612487178473/work 49 | ipython-genutils @ file:///tmp/build/80754af9/ipython_genutils_1606773439826/work 50 | isort @ file:///tmp/build/80754af9/isort_1612243941122/work 51 | jdcal==1.4.1 52 | jedi @ file:///C:/ci/jedi_1606914528444/work 53 | Jinja2 @ file:///tmp/build/80754af9/jinja2_1612213139570/work 54 | joblib @ file:///tmp/build/80754af9/joblib_1601912903842/work 55 | json5==0.9.5 56 | jsonschema @ file:///tmp/build/80754af9/jsonschema_1602607155483/work 57 | jupyter-client @ file:///tmp/build/80754af9/jupyter_client_1601311786391/work 58 | jupyter-core @ file:///C:/ci/jupyter_core_1612213356021/work 59 | jupyter-server==1.3.0 60 | jupyterlab==3.0.7 61 | jupyterlab-pygments @ file:///tmp/build/80754af9/jupyterlab_pygments_1601490720602/work 62 | jupyterlab-server==2.2.0 63 | keyring @ file:///C:/ci/keyring_1611778732215/work 64 | kiteconnect==3.9.2 65 | kiwisolver @ file:///C:/ci/kiwisolver_1612282606037/work 66 | lazy-object-proxy @ file:///C:/b/work 67 | lxml==4.6.2 68 | MarkupSafe==1.1.1 69 | matplotlib @ file:///C:/ci/matplotlib-base_1603355780617/work 70 | mccabe==0.6.1 71 | mistune==0.8.4 72 | mkl-fft==1.2.0 73 | mkl-random==1.1.1 74 | mkl-service==2.3.0 75 | multitasking==0.0.9 76 | mypy-extensions==0.4.3 77 | nbclassic==0.2.6 78 | nbclient @ file:///tmp/build/80754af9/nbclient_1602783176460/work 79 | nbconvert @ file:///C:/ci/nbconvert_1601914925608/work 80 | nbformat @ file:///tmp/build/80754af9/nbformat_1610738111109/work 81 | nest-asyncio==1.5.1 82 | nltk @ file:///tmp/build/80754af9/nltk_1592496090529/work 83 | notebook @ file:///D:/bld/notebook_1610575486218/work 84 | numpy @ file:///C:/ci/numpy_and_numpy_base_1603466732592/work 85 | numpydoc @ file:///tmp/build/80754af9/numpydoc_1605117425582/work 86 | olefile==0.46 87 | openpyxl==3.0.6 88 | packaging @ file:///tmp/build/80754af9/packaging_1611952188834/work 89 | pandas @ file:///C:/ci/pandas_1611171485393/work 90 | pandas-datareader==0.9.0 91 | pandas-ta @ git+https://github.com/twopirllc/pandas-ta@bf7e2b395596e8a75bed863e9ce0a0f34d14e829 92 | pandocfilters @ file:///C:/ci/pandocfilters_1605102497129/work 93 | paramiko @ file:///tmp/build/80754af9/paramiko_1598886428689/work 94 | parso==0.7.0 95 | pathspec==0.7.0 96 | patsy==0.5.1 97 | pexpect @ file:///tmp/build/80754af9/pexpect_1605563209008/work 98 | pickleshare @ file:///tmp/build/80754af9/pickleshare_1606932040724/work 99 | Pillow @ file:///C:/ci/pillow_1609786840597/work 100 | pluggy==0.13.1 101 | prometheus-client @ file:///tmp/build/80754af9/prometheus_client_1606344362066/work 102 | prompt-toolkit==3.0.14 103 | psutil @ file:///C:/ci/psutil_1612298324802/work 104 | ptyprocess @ file:///tmp/build/80754af9/ptyprocess_1609355006118/work/dist/ptyprocess-0.7.0-py2.py3-none-any.whl 105 | pyasn1==0.4.8 106 | pyasn1-modules==0.2.8 107 | pycodestyle @ file:///home/ktietz/src/ci_mi/pycodestyle_1612807597675/work 108 | pycparser @ file:///tmp/build/80754af9/pycparser_1594388511720/work 109 | pydocstyle @ file:///tmp/build/80754af9/pydocstyle_1598885001695/work 110 | pyflakes @ file:///home/ktietz/src/ci_ipy2/pyflakes_1612551159640/work 111 | Pygments @ file:///tmp/build/80754af9/pygments_1610565767015/work 112 | PyHamcrest==2.0.2 113 | pylint @ file:///C:/ci/pylint_1598617153160/work 114 | pyls-black @ file:///tmp/build/80754af9/pyls-black_1607553132291/work 115 | pyls-spyder @ file:///tmp/build/80754af9/pyls-spyder_1608134179673/work 116 | PyNaCl @ file:///C:/ci/pynacl_1595000047588/work 117 | pyOpenSSL @ file:///tmp/build/80754af9/pyopenssl_1608057966937/work 118 | pyparsing @ file:///home/linux1/recipes/ci/pyparsing_1610983426697/work 119 | pyrsistent @ file:///C:/ci/pyrsistent_1600141795814/work 120 | PySocks @ file:///C:/ci/pysocks_1605287845585/work 121 | python-dateutil @ file:///home/ktietz/src/ci/python-dateutil_1611928101742/work 122 | python-jsonrpc-server @ file:///tmp/build/80754af9/python-jsonrpc-server_1600278539111/work 123 | python-language-server @ file:///tmp/build/80754af9/python-language-server_1607972495879/work 124 | pytz @ file:///tmp/build/80754af9/pytz_1612215392582/work 125 | pywin32==300 126 | pywin32-ctypes==0.2.0 127 | pywinpty==0.5.7 128 | PyYAML==5.4.1 129 | pyzmq==22.0.2 130 | QDarkStyle==2.8.1 131 | QtAwesome @ file:///tmp/build/80754af9/qtawesome_1602272867890/work 132 | qtconsole @ file:///tmp/build/80754af9/qtconsole_1612458529756/work 133 | QtPy==1.9.0 134 | regex @ file:///C:/ci/regex_1606691183008/work 135 | requests @ file:///tmp/build/80754af9/requests_1608241421344/work 136 | rope @ file:///tmp/build/80754af9/rope_1602264064449/work 137 | Rtree==0.9.4 138 | scipy @ file:///C:/ci/scipy_1612469693242/work 139 | seaborn==0.11.1 140 | selenium @ file:///D:/bld/selenium_1610664674276/work 141 | Send2Trash @ file:///tmp/build/80754af9/send2trash_1607525499227/work 142 | service-identity==18.1.0 143 | sip==4.19.13 144 | six @ file:///C:/ci/six_1605187374963/work 145 | sniffio==1.2.0 146 | snowballstemmer @ file:///tmp/build/80754af9/snowballstemmer_1611258885636/work 147 | sortedcontainers @ file:///tmp/build/80754af9/sortedcontainers_1606865132123/work 148 | soupsieve==2.2 149 | Sphinx @ file:///tmp/build/80754af9/sphinx_1610133430332/work 150 | sphinxcontrib-applehelp @ file:///home/ktietz/src/ci/sphinxcontrib-applehelp_1611920841464/work 151 | sphinxcontrib-devhelp @ file:///home/ktietz/src/ci/sphinxcontrib-devhelp_1611920923094/work 152 | sphinxcontrib-htmlhelp @ file:///home/ktietz/src/ci/sphinxcontrib-htmlhelp_1611920974801/work 153 | sphinxcontrib-jsmath @ file:///home/ktietz/src/ci/sphinxcontrib-jsmath_1611920942228/work 154 | sphinxcontrib-qthelp @ file:///home/ktietz/src/ci/sphinxcontrib-qthelp_1611921055322/work 155 | sphinxcontrib-serializinghtml @ file:///home/ktietz/src/ci/sphinxcontrib-serializinghtml_1611920755253/work 156 | spyder @ file:///C:/ci/spyder_1612463691159/work 157 | spyder-kernels @ file:///C:/ci/spyder-kernels_1608560699887/work 158 | statsmodels==0.12.1 159 | TA-Lib @ file:///D:/Downloads/TA_Lib-0.4.19-cp38-cp38-win_amd64.whl 160 | termcolor==1.1.0 161 | terminado==0.9.2 162 | testpath @ file:///home/ktietz/src/ci/testpath_1611930608132/work 163 | textblob==0.15.3 164 | textdistance @ file:///tmp/build/80754af9/textdistance_1612461398012/work 165 | three-merge @ file:///tmp/build/80754af9/three-merge_1607553261110/work 166 | toml @ file:///tmp/build/80754af9/toml_1592853716807/work 167 | tornado @ file:///C:/ci/tornado_1606942392901/work 168 | tqdm @ file:///tmp/build/80754af9/tqdm_1602185206534/work 169 | traitlets @ file:///home/ktietz/src/ci/traitlets_1611929699868/work 170 | Twisted==20.3.0 171 | txaio==20.12.1 172 | typed-ast @ file:///C:/ci/typed-ast_1610466535590/work 173 | typing-extensions @ file:///home/ktietz/src/ci_mi/typing_extensions_1612808209620/work 174 | ujson @ file:///C:/ci/ujson_1611241570789/work 175 | urllib3 @ file:///tmp/build/80754af9/urllib3_1611694770489/work 176 | vaderSentiment==3.3.2 177 | watchdog @ file:///C:/ci/watchdog_1612471251191/work 178 | wcwidth @ file:///tmp/build/80754af9/wcwidth_1593447189090/work 179 | webencodings==0.5.1 180 | Werkzeug==0.15.5 181 | win-inet-pton @ file:///C:/ci/win_inet_pton_1605306167264/work 182 | wincertstore==0.2 183 | wrapt==1.11.2 184 | xlrd==2.0.1 185 | yapf @ file:///tmp/build/80754af9/yapf_1593528177422/work 186 | yfinance==0.1.55 187 | zipp @ file:///tmp/build/80754af9/zipp_1604001098328/work 188 | zope.interface==5.2.0 189 | -------------------------------------------------------------------------------- /strategy_PSAR.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | """ 3 | Created on Sat Feb 20 22:27:19 2021 4 | 5 | @author: pramay 6 | """ 7 | # to be on safe side start at 9:16:15 i.e 1min 15 sec late than candle start 8 | from kiteconnect import KiteConnect 9 | import os 10 | import pandas as pd 11 | import time 12 | import traceback 13 | 14 | # from utils import * 15 | from utils import fetchOHLC, PSAR, getTickSize, placeGTTOrder, placeMarketOrder, notify 16 | cwd = os.getcwd() 17 | 18 | # generate trading session 19 | access_token = open("access_token.txt", 'r').read() 20 | key_secret = open("api_key.txt", 'r').read().split() 21 | kite = KiteConnect(api_key=key_secret[0]) 22 | kite.set_access_token(access_token) 23 | 24 | NSE_NFO = "NSE" 25 | LOT_SIZE = 1 26 | if NSE_NFO == "NFO": 27 | LOT_SIZE = 25 28 | n_shares = 1 29 | quantity = n_shares*LOT_SIZE # multiple of LOT_SIZE 30 | capital = 3000 31 | # tickers = ["INFY"] 32 | tickers = ["AXISBANK"] 33 | tickers = ["INDUSINDBK"] 34 | ticker = tickers[0] 35 | exchange_type = kite.EXCHANGE_NSE 36 | if NSE_NFO == "NFO": 37 | exchange_type = kite.EXCHANGE_NFO 38 | minute = 15 39 | minute_code = "minute" 40 | if minute > 1: 41 | minute_code = str(minute)+minute_code 42 | 43 | trigger_id_gtt = {ticker: None for ticker in tickers} 44 | 45 | product_type = kite.PRODUCT_MIS # or kite.PRODUCT_NRML 46 | 47 | # get dump of all nfo instruments 48 | instrument_dump = kite.instruments(NSE_NFO) 49 | instrument_df = pd.DataFrame(instrument_dump) 50 | 51 | 52 | def main(): 53 | a = 0 54 | b = 0 55 | while a < 10: 56 | try: 57 | pos_df = pd.DataFrame(kite.positions()["day"]) 58 | break 59 | except: 60 | print("can't extract position data..retrying") 61 | a += 1 62 | while b < 10: 63 | try: 64 | ord_df = pd.DataFrame(kite.orders()) 65 | # print("Orders --- ") 66 | # print(ord_df) 67 | break 68 | except: 69 | print("can't extract order data..retrying") 70 | b += 1 71 | # for ticker in tickers: 72 | print("\n**************************************************************") 73 | print("starting passthrough for {} at {}".format( 74 | ticker, time.asctime(time.localtime(time.time())))) 75 | 76 | try: 77 | a = 0 78 | while a < 10: 79 | try: 80 | ohlc = fetchOHLC(kite, instrument_df, ticker, minute_code, 30) 81 | break 82 | except: 83 | print("can't extract position data..retrying") 84 | a += 1 85 | init_len = len(ohlc) 86 | ohlc['psar'] = PSAR(ohlc) 87 | ltp = kite.ltp(NSE_NFO+':' + ticker)[NSE_NFO+':' + ticker]['last_price'] 88 | tick_size = getTickSize(instrument_df, ticker) 89 | 90 | if ohlc['low'][-1] > ohlc['psar'][-1]: 91 | print("PSAR is below candle") 92 | elif (ohlc['high'][-1] < ohlc['psar'][-1]): 93 | print("PSAR is above candle") 94 | 95 | print("PSAR", ohlc['psar'][-1]) 96 | print("Current Price - ", ltp) 97 | print("OPEN ", ohlc['open'][-1]) 98 | print("Close ", ohlc['close'][-1]) 99 | print("High ", ohlc['high'][-1]) 100 | print("low ", ohlc['low'][-1]) 101 | 102 | c1 = len(pos_df.columns) == 0 103 | c2 = (len(pos_df.columns) != 0 and ticker not in pos_df["tradingsymbol"].tolist()) 104 | c3 = (len(pos_df.columns) != 0 and ticker in pos_df["tradingsymbol"].tolist()) and ( 105 | pos_df[pos_df["tradingsymbol"] == ticker]["quantity"].values[0] == 0) 106 | 107 | # ltp_sell = tick_size * round((ltp-2)/tick_size) 108 | # sl_sell = tick_size * round((1.05*ltp) /tick_size) 109 | # strategy 110 | if c1 or c2 or c3: 111 | # BUY 112 | 113 | # when psar is below the candle when the condition checked 114 | if (ohlc['low'][-1] > ohlc['psar'][-1]) and (ohlc['low'][-2] < ohlc['psar'][-2]): 115 | # https://kite.trade/forum/discussion/9299/set-a-stop-loss-and-target-at-same-time#latest 116 | # https://kite.trade/forum/discussion/9300/receiving-this-order-price-is-not-a-multiple-of-tick-size#latest 117 | ltp_ = ohlc['high'][-2] + 2 118 | ltp_buy = tick_size * round((ltp_)/tick_size) 119 | sl_buy = tick_size * round((ltp_buy - 20)/tick_size) 120 | 121 | placeGTTOrder(kite, ticker, "buy", quantity, sl_buy, ltp_buy, 122 | exchange_type, trigger_id_gtt, product_type, ltp) 123 | a = 0 124 | b = 0 125 | while a < 10: 126 | try: 127 | pos_df = pd.DataFrame(kite.positions()["day"]) 128 | break 129 | except: 130 | print("can't extract position data..retrying") 131 | a += 1 132 | while b < 10: 133 | try: 134 | ord_df = pd.DataFrame(kite.orders()) 135 | break 136 | except: 137 | print("can't extract order data..retrying") 138 | b += 1 139 | 140 | # when psar is above candle when condition checked but in between moves below 141 | # this case will be missed in above condition as when above condition checks already a new candle is formed. 142 | 143 | # for Ex. at 9:15:10 we start our code and PSAR is above the candle. Now we check again at 10:15:10 and PSAR is still above 144 | # the candle so it again goes to sleep. But now at 10:30 PSAR comes below the cnadle, 145 | # then when the code checks again at 11:15:10 the PSAR is below and also prev PSAR(10:15) is below so it wont buy. 146 | # But we have to buy at high(10:15)+2 147 | 148 | # if already order pending then break 149 | d = ord_df.loc[(ord_df['tradingsymbol'] == ticker) & ( 150 | ord_df['status'].isin(["TRIGGER PENDING", "OPEN", "PENDING"]))] 151 | 152 | if (d.empty) and (ohlc['low'][-1] > ohlc['psar'][-1]) and (ohlc['low'][-2] > ohlc['psar'][-2]) and (ohlc['low'][-3] < ohlc['psar'][-3]): 153 | print("BUY was missed but taken care of !!") 154 | ltp_ = ohlc['high'][-2] + 2 155 | ltp_buy = tick_size * round((ltp_)/tick_size) 156 | sl_buy = tick_size * round((ltp_buy - 20)/tick_size) 157 | 158 | placeGTTOrder(kite, ticker, "buy", quantity, sl_buy, ltp_buy, 159 | exchange_type, trigger_id_gtt, product_type, ltp) 160 | pos_df = pd.DataFrame(kite.positions()["day"]) 161 | 162 | # current candle is below , prev is also below and prev prev is above 163 | 164 | c4 = (len(pos_df.columns) != 0 and ticker in pos_df["tradingsymbol"].tolist()) and ( 165 | pos_df[pos_df["tradingsymbol"] == ticker]["quantity"].values[0] != 0) 166 | 167 | if c4: 168 | # means already buy or sell in positions 169 | print(" Checking for sell ") 170 | trigger_id = trigger_id_gtt[ticker] 171 | 172 | a = 0 173 | while a < 10: 174 | try: 175 | pos_df = pd.DataFrame(kite.positions()["day"]) 176 | break 177 | except: 178 | print("can't extract position data..retrying") 179 | a += 1 180 | 181 | # if already buy 182 | # 2nd condition if sl hit then move out from loop 183 | while (pos_df[pos_df["tradingsymbol"] == ticker]["quantity"].values[0] != 0): 184 | a = 0 185 | while a < 10: 186 | try: 187 | ohlc_live = fetchOHLC(kite, instrument_df, ticker, minute_code, 30) 188 | break 189 | except: 190 | print("can't extract ohlc data..retrying") 191 | a += 1 192 | 193 | ohlc_live['psar'] = PSAR(ohlc_live) 194 | 195 | if len(ohlc_live) == (init_len + 1): 196 | print("Breaking out -- {} ".format(time.asctime(time.localtime(time.time())))) 197 | break 198 | if (pos_df[pos_df["tradingsymbol"] == ticker]["quantity"].values[0] > 0) and ((ohlc_live['high'][-1] < ohlc_live['psar'][-1]) and (ohlc_live['high'][-2] > ohlc_live['psar'][-2])): 199 | placeMarketOrder(kite, ticker, "sell", quantity, 200 | exchange_type, product_type) 201 | 202 | if trigger_id: 203 | print("Deleting GTT") 204 | kite.delete_gtt(trigger_id) 205 | 206 | break 207 | a = 0 208 | while a < 10: 209 | try: 210 | pos_df = pd.DataFrame(kite.positions()["day"]) 211 | break 212 | except: 213 | print("can't extract position data..retrying") 214 | a += 1 215 | 216 | except Exception: 217 | notify(error=True) 218 | notify(error=True) 219 | notify(error=True) 220 | print("API error for ticker :", ticker) 221 | print(traceback.format_exc()) 222 | 223 | 224 | starttime = time.time() 225 | timeout = time.time() + 60*60*6 # 60 seconds times 360 meaning 6 hrs 226 | while time.time() <= timeout: 227 | try: 228 | main() 229 | print("Sleeping for ", max(0, (60*minute - ((time.time() - starttime) % (60.0*minute))))) 230 | time.sleep(max(0, (60*minute - ((time.time() - starttime) % (60.0*minute))))) 231 | except KeyboardInterrupt: 232 | print('\n\nKeyboard exception received. Exiting.') 233 | import sys 234 | sys.exit() 235 | -------------------------------------------------------------------------------- /utils.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | """ 3 | Created on Fri Feb 19 14:22:14 2021 4 | 5 | @author: pramay 6 | """ 7 | 8 | import datetime as dt 9 | import pandas as pd 10 | import numpy as np 11 | import time 12 | import talib 13 | from termcolor import colored 14 | import pandas_ta as ta 15 | 16 | 17 | def instrumentLookup(instrument_df, symbol): 18 | """Looks up instrument token for a given script from instrument dump""" 19 | try: 20 | return instrument_df[instrument_df.tradingsymbol == symbol].instrument_token.values[0] 21 | except: 22 | return -1 23 | 24 | 25 | def getTickSize(instrument_df, symbol): 26 | """gets the tick size for a given script from instrument dump""" 27 | try: 28 | return instrument_df[instrument_df.tradingsymbol == symbol].tick_size.values[0] 29 | except: 30 | return -1 31 | 32 | 33 | def fetchOHLC(kite, instrument_df, ticker, interval, duration): 34 | """extracts historical data and outputs in the form of dataframe""" 35 | instrument = instrumentLookup(instrument_df, ticker) 36 | data = pd.DataFrame(kite.historical_data(instrument, dt.date.today() - 37 | dt.timedelta(duration), dt.date.today(), interval)) 38 | # data.drop(data.tail(1).index, inplace=True) 39 | data.set_index("date", inplace=True) 40 | return data 41 | 42 | 43 | def rsi(df, n): 44 | "function to calculate RSI" 45 | delta = df["close"].diff().dropna() 46 | u = delta * 0 47 | d = u.copy() 48 | u[delta > 0] = delta[delta > 0] 49 | d[delta < 0] = -delta[delta < 0] 50 | u[u.index[n-1]] = np.mean(u[:n]) # first value is average of gains 51 | u = u.drop(u.index[:(n-1)]) 52 | d[d.index[n-1]] = np.mean(d[:n]) # first value is average of losses 53 | d = d.drop(d.index[:(n-1)]) 54 | rs = u.ewm(com=n, min_periods=n).mean()/d.ewm(com=n, min_periods=n).mean() 55 | return 100 - 100 / (1+rs) 56 | 57 | 58 | def PSAR_V1(data, acc=0.02, maxi=0.2): 59 | return talib.SAR(data['high'], data['low'], acceleration=acc, maximum=maxi) 60 | 61 | 62 | def PSAR(data): 63 | # https://github.com/mrjbq7/ta-lib/issues/196 64 | # https://github.com/twopirllc/pandas-ta#installation 65 | temp_data = data.ta.psar() 66 | return temp_data['PSARl_0.02_0.2'].combine_first(temp_data['PSARs_0.02_0.2']) 67 | 68 | 69 | def notify(error=False): 70 | """make sound to notify""" 71 | 72 | import winsound 73 | if not error: 74 | frequency = 500 # Set Frequency To 2500 Hertz 75 | duration = 200 # Set Duration To 1000 ms == 1 second 76 | else: 77 | frequency = 500 # Set Frequency To 2500 Hertz 78 | duration = 100 # Set Duration To 1000 ms == 1 second 79 | 80 | winsound.Beep(frequency, duration) 81 | 82 | 83 | def notifyGTTOrder(kite, symbol, buy_sell, quantity, sl_price, ltp, exchange, trigger_id_gtt, product_type, target=None): 84 | # make sound to notify 85 | notify() 86 | if buy_sell == "buy": 87 | print(colored("{} - Placing {} order at {}. Given Price - {}".format(symbol, 88 | buy_sell, time.asctime(time.localtime(time.time())), ltp), 'green', 'on_grey')) 89 | else: 90 | print(colored("{} - Placing {} order at {}. Given Price - {}".format(symbol, 91 | buy_sell, time.asctime(time.localtime(time.time())), ltp), 'red', 'on_grey')) 92 | 93 | if buy_sell == "buy": 94 | t_type = kite.TRANSACTION_TYPE_BUY 95 | t_type_sl = kite.TRANSACTION_TYPE_SELL 96 | elif buy_sell == "sell": 97 | t_type = kite.TRANSACTION_TYPE_SELL 98 | t_type_sl = kite.TRANSACTION_TYPE_BUY 99 | 100 | print("Placing GTT order type {} at {}".format( 101 | t_type_sl, time.asctime(time.localtime(time.time())))) 102 | print("SL Price ", sl_price) 103 | 104 | 105 | def placeGTTOrder(kite, symbol, buy_sell, quantity, sl_price, buy_price, exchange, trigger_id_gtt, product_type, current_price, target=None): 106 | # make sound to notify 107 | notify() 108 | if buy_sell == "buy": 109 | print(colored("{} - Placing {} order at {}. Given Price - {}".format(symbol, 110 | buy_sell, time.asctime(time.localtime(time.time())), buy_price), 'green', 'on_grey')) 111 | else: 112 | print(colored("{} - Placing {} order at {}. Given Price - {}".format(symbol, 113 | buy_sell, time.asctime(time.localtime(time.time())), buy_price), 'red', 'on_grey')) 114 | 115 | # print("{} - Placing {} order at {}".format(symbol, buy_sell, time.asctime( time.localtime(time.time())))) 116 | 117 | if buy_sell == "buy": 118 | t_type = kite.TRANSACTION_TYPE_BUY 119 | t_type_sl = kite.TRANSACTION_TYPE_SELL 120 | elif buy_sell == "sell": 121 | t_type = kite.TRANSACTION_TYPE_SELL 122 | t_type_sl = kite.TRANSACTION_TYPE_BUY 123 | 124 | if buy_price < current_price: 125 | # place limit for different price 126 | # https://kite.trade/forum/discussion/9328/buy-price-in-kite-place-order#latest 127 | print("LIMIT as current price is higher that the desired buy price") 128 | kite.place_order(tradingsymbol=symbol, 129 | exchange=exchange, 130 | transaction_type=t_type, 131 | quantity=quantity, 132 | order_type=kite.ORDER_TYPE_LIMIT, 133 | price=buy_price, 134 | product=product_type, 135 | variety=kite.VARIETY_REGULAR, 136 | validity=kite.VALIDITY_DAY) 137 | else: 138 | print("SL as the buy_price is higher than the current price") 139 | kite.place_order(tradingsymbol=symbol, 140 | exchange=exchange, 141 | transaction_type=t_type, 142 | quantity=quantity, 143 | order_type=kite.ORDER_TYPE_SL, 144 | price=buy_price, 145 | trigger_price=buy_price, 146 | product=product_type, 147 | variety=kite.VARIETY_REGULAR, 148 | validity=kite.VALIDITY_DAY) 149 | 150 | order_dict = [{"transaction_type": t_type_sl, "quantity": quantity, 151 | 'order_type': kite.ORDER_TYPE_LIMIT, "product": product_type, "price": sl_price}, 152 | {"transaction_type": t_type_sl, "quantity": quantity, 153 | 'order_type': kite.ORDER_TYPE_LIMIT, "product": product_type, "price": target} 154 | ] 155 | print("Placing GTT order type {} at {}".format( 156 | t_type_sl, time.asctime(time.localtime(time.time())))) 157 | 158 | # trigger_id = kite.place_gtt(kite.GTT_TYPE_OCO, symbol, exchange, [sl_price, target], ltp, order_dict) 159 | 160 | trigger_id = kite.place_gtt(kite.GTT_TYPE_SINGLE, symbol, exchange, [ 161 | sl_price], buy_price, [order_dict[0]]) 162 | trigger_id_gtt[symbol] = trigger_id['trigger_id'] 163 | print() 164 | print(trigger_id_gtt) 165 | 166 | 167 | def notifyMarketOrder(kite, symbol, buy_sell, quantity, exchange, product_type): 168 | # make sound to notify 169 | notify() 170 | 171 | if buy_sell == "buy": 172 | print(colored("{} - Placing Market {} order at {}".format(symbol, buy_sell, 173 | time.asctime(time.localtime(time.time()))), 'cyan', 'on_grey')) 174 | else: 175 | print(colored("{} - Placing Market {} order at {}".format(symbol, buy_sell, 176 | time.asctime(time.localtime(time.time()))), 'yellow', 'on_grey')) 177 | 178 | 179 | def placeMarketOrder(kite, symbol, buy_sell, quantity, exchange, product_type): 180 | # make sound to notify 181 | notify() 182 | 183 | if buy_sell == "buy": 184 | print(colored("{} - Placing Market {} order at {}".format(symbol, buy_sell, 185 | time.asctime(time.localtime(time.time()))), 'cyan', 'on_grey')) 186 | else: 187 | print(colored("{} - Placing Market {} order at {}".format(symbol, buy_sell, 188 | time.asctime(time.localtime(time.time()))), 'yellow', 'on_grey')) 189 | 190 | if buy_sell == "buy": 191 | t_type = kite.TRANSACTION_TYPE_BUY 192 | elif buy_sell == "sell": 193 | t_type = kite.TRANSACTION_TYPE_SELL 194 | 195 | # place normal market order 196 | kite.place_order(tradingsymbol=symbol, 197 | exchange=exchange, 198 | transaction_type=t_type, 199 | quantity=quantity, 200 | order_type=kite.ORDER_TYPE_MARKET, 201 | product=product_type, 202 | variety=kite.VARIETY_REGULAR) 203 | 204 | 205 | def ModifyOrder(kite, symbol, order_id, price): 206 | # Modify order given order id 207 | print("{} - Modifying order at {}".format(symbol, time.asctime(time.localtime(time.time())))) 208 | print("Setting Stop Loss ", price) 209 | kite.modify_order(order_id=order_id, 210 | price=price, 211 | trigger_price=price, 212 | order_type=kite.ORDER_TYPE_SL, 213 | variety=kite.VARIETY_REGULAR) 214 | --------------------------------------------------------------------------------