├── .gitignore ├── LICENSE ├── README.md ├── create_db.sql ├── db.py └── stream.py /.gitignore: -------------------------------------------------------------------------------- 1 | # Byte-compiled / optimized / DLL files 2 | __pycache__/ 3 | *.py[cod] 4 | *$py.class 5 | 6 | # C extensions 7 | *.so 8 | 9 | # Distribution / packaging 10 | .Python 11 | env/ 12 | build/ 13 | develop-eggs/ 14 | dist/ 15 | downloads/ 16 | eggs/ 17 | .eggs/ 18 | lib/ 19 | lib64/ 20 | parts/ 21 | sdist/ 22 | var/ 23 | wheels/ 24 | *.egg-info/ 25 | .installed.cfg 26 | *.egg 27 | 28 | # PyInstaller 29 | # Usually these files are written by a python script from a template 30 | # before PyInstaller builds the exe, so as to inject date/other infos into it. 31 | *.manifest 32 | *.spec 33 | 34 | # Installer logs 35 | pip-log.txt 36 | pip-delete-this-directory.txt 37 | 38 | # Unit test / coverage reports 39 | htmlcov/ 40 | .tox/ 41 | .coverage 42 | .coverage.* 43 | .cache 44 | nosetests.xml 45 | coverage.xml 46 | *.cover 47 | .hypothesis/ 48 | 49 | # Translations 50 | *.mo 51 | *.pot 52 | 53 | # Django stuff: 54 | *.log 55 | local_settings.py 56 | 57 | # Flask stuff: 58 | instance/ 59 | .webassets-cache 60 | 61 | # Scrapy stuff: 62 | .scrapy 63 | 64 | # Sphinx documentation 65 | docs/_build/ 66 | 67 | # PyBuilder 68 | target/ 69 | 70 | # Jupyter Notebook 71 | .ipynb_checkpoints 72 | 73 | # pyenv 74 | .python-version 75 | 76 | # celery beat schedule file 77 | celerybeat-schedule 78 | 79 | # SageMath parsed files 80 | *.sage.py 81 | 82 | # dotenv 83 | .env 84 | 85 | # virtualenv 86 | .venv 87 | venv/ 88 | ENV/ 89 | 90 | # Spyder project settings 91 | .spyderproject 92 | .spyproject 93 | 94 | # Rope project settings 95 | .ropeproject 96 | 97 | # mkdocs documentation 98 | /site 99 | 100 | # mypy 101 | .mypy_cache/ 102 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2017 Vivek R 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 | # Kite connect python client example 2 | 3 | This is a simple example which uses [Python Kite connect client](https://github.com/rainmattertech/pykiteconnect) to receive ticks and save it to [Postgresql](https://www.postgresql.org/) database. 4 | Celery is used as a Task queue manager to insert to database without blocking main Kite connect WebSocket thread. 5 | 6 | Kite ticker subscribes to tokens in specified in `stream.py` with 5 second delay. Ticks received are sent to 7 | celery taske queue where it will be inserted to db. 8 | 9 | # Requirements 10 | 11 | 1. [Redis](https://redis.io) or any [AMQP client for Celery](http://docs.celeryproject.org/en/latest/getting-started/brokers/) 12 | 2. [Postgresql](https://www.postgresql.org/) db (Can be replaced with any other db) 13 | 14 | # Install 15 | 16 | ``` 17 | pip install celery 18 | pip install psycopg2 19 | pip install kiteconnect 20 | ``` 21 | 22 | # Create database and table 23 | 24 | Create a database called `ticks` 25 | 26 | ``` 27 | CREATE DATABASE ticks; 28 | ``` 29 | 30 | Create a table called `ticks` in `ticks` database 31 | 32 | ``` 33 | CREATE TABLE ticks ( 34 | token integer NOT NULL, 35 | date timestamp without time zone, 36 | price double precision 37 | ); 38 | ``` 39 | 40 | # Configure celery and database in db.py 41 | 42 | 1. Update `broker` URL in `db.py` with redis or any other AMQP client. 43 | 2. Update `user`, `password` and `host` details for Postgresql in `db.py` 44 | 45 | # Run Celery worker 46 | 47 | ``` 48 | celery -A db worker --loglevel=info 49 | ``` 50 | 51 | # Run Python client 52 | 53 | ``` 54 | python stream.py 55 | ``` 56 | -------------------------------------------------------------------------------- /create_db.sql: -------------------------------------------------------------------------------- 1 | CREATE DATABASE ticks; 2 | CREATE TABLE ticks ( 3 | token integer NOT NULL, 4 | date timestamp without time zone, 5 | price double precision 6 | ); 7 | -------------------------------------------------------------------------------- /db.py: -------------------------------------------------------------------------------- 1 | # Run celery workers 2 | # celery -A db worker --loglevel=info 3 | 4 | import sys 5 | import json 6 | import psycopg2 7 | import logging 8 | from celery import Celery 9 | from datetime import datetime 10 | 11 | logging.basicConfig(stream=sys.stdout, level=logging.DEBUG, 12 | format="%(asctime)s - %(name)s - %(levelname)s - %(message)s") 13 | 14 | # Configure with your own broker 15 | app = Celery("tasks", broker="redis://localhost:6379/4") 16 | 17 | # Initialize db 18 | db = psycopg2.connect(database="ticks", user="postgres", password="password", host="127.0.0.1", port="5432") 19 | 20 | # Db insert statement 21 | insert_tick_statement = "INSERT INTO ticks (date, token, price) VALUES (%(date)s, %(token)s, %(price)s)" 22 | 23 | 24 | # Task to insert to SQLite db 25 | @app.task 26 | def insert_ticks(ticks): 27 | c = db.cursor() 28 | for tick in ticks: 29 | c.execute(insert_tick_statement, { 30 | "date": datetime.now(), 31 | "token": tick["instrument_token"], 32 | "price": tick["last_price"]}) 33 | 34 | logging.info("Inserting ticks to db : {}".format(json.dumps(ticks))) 35 | 36 | try: 37 | db.commit() 38 | except Exception: 39 | db.rollback() 40 | logging.exception("Couldn't write ticks to db: ") 41 | -------------------------------------------------------------------------------- /stream.py: -------------------------------------------------------------------------------- 1 | import sys 2 | import json 3 | import logging 4 | logging.basicConfig(stream=sys.stdout, level=logging.DEBUG, 5 | format="%(asctime)s - %(name)s - %(levelname)s - %(message)s") 6 | 7 | import time 8 | from db import insert_ticks 9 | from kiteconnect import KiteTicker 10 | 11 | # Initialise. 12 | kws = KiteTicker("your_api_key", "your_access_token") 13 | 14 | # SBIN NSE, RELIANCE BSE, NIFTY 50, SENSEX 15 | tokens = [779521, 128083204, 256265, 265] 16 | 17 | 18 | # Callback for tick reception. 19 | def on_tick(ws, ticks): 20 | logging.info("on tick - {}".format(json.dumps(ticks))) 21 | insert_ticks.delay(ticks) 22 | 23 | 24 | # Callback for successful connection. 25 | def on_connect(ws, response): 26 | logging.info("Successfully connected to WebSocket") 27 | 28 | 29 | def on_close(ws, code, reason): 30 | logging.info("WebSocket connection closed") 31 | 32 | 33 | def on_error(ws, code, reason): 34 | logging.info("Connection error: {code} - {reason}".format(code=code, reason=reason)) 35 | 36 | # Callback when reconnect is on progress 37 | def on_reconnect(ws, attempts_count): 38 | logging.info("Reconnecting: {}".format(attempts_count)) 39 | 40 | # Assign the callbacks. 41 | kws.on_tick = on_tick 42 | kws.on_connect = on_connect 43 | kws.on_close = on_close 44 | kws.on_error = on_error 45 | kws.on_reconnect = on_reconnect 46 | 47 | # Infinite loop on the main thread. Nothing after this will run. 48 | # You have to use the pre-defined callbacks to manage subscriptions. 49 | kws.connect(threaded=True) 50 | # kws.connect(disable_ssl_verification=True) # for ubuntu 51 | 52 | count = 0 53 | while True: 54 | logging.info("This is main thread. Will subscribe to each token in tokens list with 5s delay") 55 | 56 | if count < len(tokens): 57 | if kws.is_connected(): 58 | logging.info("Subscribing to: {}".format(tokens[count])) 59 | kws.subscribe([tokens[count]]) 60 | kws.set_mode(kws.MODE_LTP, [tokens[count]]) 61 | count += 1 62 | else: 63 | logging.info("Connecting to WebSocket...") 64 | 65 | time.sleep(5) 66 | --------------------------------------------------------------------------------