├── __init__.py ├── broker ├── __init__.py ├── dhan │ ├── __init__.py │ ├── api │ │ ├── __init__.py │ │ └── baseurl.py │ ├── plugin.json │ └── streaming │ │ └── __init__.py ├── zebu │ ├── __init__.py │ ├── api │ │ ├── __init__.py │ │ └── margin_api.py │ ├── plugin.json │ ├── streaming │ │ └── __init__.py │ └── mapping │ │ └── margin_data.py ├── angel │ ├── __init__.py │ ├── api │ │ ├── __init__.py │ │ ├── funds.py │ │ └── auth_api.py │ └── plugin.json ├── firstock │ ├── __init__.py │ ├── api │ │ ├── __init__.py │ │ └── margin_api.py │ ├── plugin.json │ ├── streaming │ │ ├── __init__.py │ │ └── firstock_mapping.py │ └── mapping │ │ └── margin_data.py ├── fivepaisa │ ├── __init__.py │ ├── api │ │ ├── __init__.py │ │ └── margin_api.py │ ├── plugin.json │ ├── streaming │ │ └── __init__.py │ └── mapping │ │ └── margin_data.py ├── flattrade │ ├── __init__.py │ ├── api │ │ └── __init__.py │ ├── plugin.json │ └── streaming │ │ └── __init__.py ├── fyers │ ├── api │ │ └── __init__.py │ ├── streaming │ │ └── __init__.py │ └── plugin.json ├── groww │ ├── __init__.py │ ├── api │ │ └── __init__.py │ ├── plugin.json │ └── streaming │ │ └── __init__.py ├── iifl │ ├── api │ │ ├── __init__.py │ │ └── margin_api.py │ ├── streaming │ │ └── __init__.py │ ├── plugin.json │ ├── baseurl.py │ └── mapping │ │ └── margin_data.py ├── indmoney │ ├── __init__.py │ ├── api │ │ ├── __init__.py │ │ ├── baseurl.py │ │ └── auth_api.py │ ├── plugin.json │ └── streaming │ │ └── __init__.py ├── kotak │ ├── __init__.py │ ├── api │ │ └── __init__.py │ ├── plugin.json │ └── streaming │ │ └── __init__.py ├── motilal │ ├── __init__.py │ ├── api │ │ ├── __init__.py │ │ └── margin_api.py │ ├── plugin.json │ ├── streaming │ │ └── __init__.py │ └── mapping │ │ └── margin_data.py ├── mstock │ ├── __init__.py │ ├── api │ │ └── __init__.py │ ├── plugin.json │ ├── streaming │ │ └── __init__.py │ └── remainwork.md ├── paytm │ ├── api │ │ ├── __init__.py │ │ └── margin_api.py │ ├── plugin.json │ ├── streaming │ │ └── __init__.py │ └── mapping │ │ └── margin_data.py ├── shoonya │ ├── __init__.py │ ├── api │ │ ├── __init__.py │ │ └── auth_api.py │ ├── plugin.json │ └── streaming │ │ └── __init__.py ├── tradejini │ ├── __init__.py │ ├── api │ │ ├── __init__.py │ │ └── margin_api.py │ ├── plugin.json │ ├── streaming │ │ └── __init__.py │ └── mapping │ │ └── margin_data.py ├── upstox │ ├── __init__.py │ ├── api │ │ └── __init__.py │ └── plugin.json ├── aliceblue │ ├── api │ │ ├── __init__.py │ │ └── margin_api.py │ ├── plugin.json │ ├── streaming │ │ └── __init__.py │ └── mapping │ │ └── margin_data.py ├── dhan_sandbox │ ├── __init__.py │ ├── api │ │ ├── __init__.py │ │ ├── baseurl.py │ │ └── auth_api.py │ ├── streaming │ │ ├── __init__.py │ │ └── dhan_mapping.py │ └── plugin.json ├── ibulls │ ├── api │ │ ├── __init__.py │ │ └── margin_api.py │ ├── streaming │ │ └── __init__.py │ ├── plugin.json │ ├── baseurl.py │ └── mapping │ │ └── margin_data.py ├── jainamxts │ ├── api │ │ ├── __init__.py │ │ └── margin_api.py │ ├── streaming │ │ └── __init__.py │ ├── plugin.json │ ├── baseurl.py │ └── mapping │ │ └── margin_data.py ├── pocketful │ ├── api │ │ ├── __init__.py │ │ └── margin_api.py │ ├── plugin.json │ └── mapping │ │ └── margin_data.py ├── samco │ ├── mapping │ │ └── __init__.py │ ├── __init__.py │ ├── api │ │ ├── __init__.py │ │ └── funds.py │ ├── database │ │ └── __init__.py │ └── streaming │ │ └── __init__.py ├── wisdom │ ├── api │ │ ├── __init__.py │ │ └── margin_api.py │ ├── streaming │ │ └── __init__.py │ ├── plugin.json │ ├── baseurl.py │ └── mapping │ │ └── margin_data.py ├── zerodha │ ├── api │ │ └── __init__.py │ ├── plugin.json │ └── streaming │ │ └── __init__.py ├── compositedge │ ├── api │ │ ├── __init__.py │ │ └── margin_api.py │ ├── streaming │ │ └── __init__.py │ ├── plugin.json │ ├── baseurl.py │ └── mapping │ │ └── margin_data.py ├── fivepaisaxts │ ├── api │ │ ├── __init__.py │ │ └── margin_api.py │ ├── streaming │ │ └── __init__.py │ ├── plugin.json │ ├── baseurl.py │ └── mapping │ │ └── margin_data.py └── definedge │ ├── api │ └── __init__.py │ ├── database │ └── __init__.py │ ├── mapping │ ├── __init__.py │ └── symbol_map.py │ ├── streaming │ └── __init__.py │ ├── __init__.py │ └── plugin.json ├── utils ├── __init__.py ├── version.py ├── config.py ├── plugin_loader.py ├── number_formatter.py ├── socketio_error_handler.py └── security_middleware.py ├── blueprints ├── __init__.py ├── platforms.py └── settings.py ├── database ├── __init__.py ├── tv_search.py ├── db_init_helper.py └── token_db.py ├── download ├── duckdb_downloader.py ├── symbols.csv ├── .sample.env └── README.md ├── log ├── readme.md └── strategies │ └── .gitignore ├── collections ├── openalgo │ ├── collection.bru │ ├── bruno.json │ ├── ping.bru │ ├── ticker.bru │ ├── chart_get.bru │ ├── analyzer_status.bru │ ├── analyzer_toggle.bru │ ├── funds.bru │ ├── symbol.bru │ ├── search.bru │ ├── holdings.bru │ ├── orderbook.bru │ ├── tradebook.bru │ ├── instruments.bru │ ├── intervals.bru │ ├── orderstatus.bru │ ├── positionbook.bru │ ├── optiongreeks.bru │ ├── expiry.bru │ ├── DATA │ │ └── optionchain.bru │ ├── quotes.bru │ ├── depth.bru │ ├── CancelAllOrder.bru │ ├── syntheticfuture.bru │ ├── CloseAllPositions.bru │ ├── telegram.bru │ ├── CancelOrder.bru │ ├── chart_update.bru │ ├── optionsymbol.bru │ ├── openposition.bru │ ├── history (EOD).bru │ ├── history (intraday).bru │ ├── SplitOrder.bru │ ├── margin.bru │ ├── optionsorder.bru │ ├── PlaceOrder.bru │ ├── Chartink.bru │ ├── PlaceSmartOrder.bru │ ├── ModifyOrder.bru │ ├── multiquotes.bru │ ├── BasketOrder.bru │ └── optionsmultiorder.bru └── postman │ └── openalgo.postman_environment.json ├── examples ├── go │ └── readme.md ├── nodejs │ └── readme.md └── python │ ├── multiquotes_example.py │ ├── depth_example.py │ ├── ltp_example.py │ ├── quote_example.py │ ├── placing ATM order.py │ ├── depth_50_example.py │ ├── depth_20_example.py │ └── optionchain_example.py ├── db └── readme.txt ├── tmp └── README.md ├── test ├── sandbox │ ├── __init__.py │ └── test_rejected_order.py ├── test_broadcast_save.py ├── test_bot_web.py ├── test_history_format.py ├── test_mstock.py ├── test_telegram_config.py ├── test_telegram_bot.py └── test_log_location.py ├── static ├── favicon │ ├── logo.png │ ├── favicon.ico │ ├── favicon-16x16.png │ ├── favicon-32x32.png │ ├── mstile-150x150.png │ ├── apple-touch-icon.png │ ├── android-chrome-192x192.png │ ├── browserconfig.xml │ ├── site.webmanifest │ └── safari-pinned-tab.svg ├── images │ ├── image.png │ ├── yoga.png │ ├── copy-icon.png │ ├── thumbs-up.png │ └── brokers │ │ ├── angel.png │ │ ├── dhan.png │ │ ├── fyers.png │ │ ├── icici.png │ │ ├── kotak.png │ │ ├── 5paisa.png │ │ ├── upstox.png │ │ ├── zerodha.png │ │ └── aliceblue.png ├── sounds │ └── alert.mp3 └── js │ ├── mobile-menu.js │ └── app.js ├── docs ├── architecture-diagram.png ├── prompt │ └── order-constants.md └── docker_env_changes.md ├── playground ├── Playground-screenshot.png └── style.css ├── keys └── .gitignore ├── postcss.config.mjs ├── templates ├── components │ ├── loading_spinner.html │ └── pagination.html ├── logs.html └── index.html ├── tailwind.config.mjs ├── limiter.py ├── SECURITY.md ├── strategies ├── .gitignore └── scripts │ └── .gitignore ├── extensions.py ├── utils.py ├── .dockerignore ├── sandbox └── __init__.py ├── .gitignore ├── .ebextensions └── 01_flask.config ├── docker-compose.yaml ├── package.json ├── restx_api ├── account_schema.py ├── place_order.py ├── tradebook.py ├── holdings.py ├── ping.py ├── orderbook.py ├── intervals.py ├── positionbook.py ├── funds.py ├── multiquotes.py ├── quotes.py ├── depth.py ├── search.py ├── symbol.py ├── expiry.py └── history.py ├── .claude └── settings.local.json └── cors.py /__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /broker/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /utils/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /blueprints/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /broker/dhan/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /broker/zebu/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /database/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /broker/angel/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /broker/angel/api/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /broker/dhan/api/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /broker/firstock/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /broker/fivepaisa/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /broker/flattrade/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /broker/fyers/api/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /broker/groww/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /broker/groww/api/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /broker/iifl/api/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /broker/indmoney/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /broker/kotak/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /broker/kotak/api/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /broker/motilal/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /broker/mstock/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /broker/paytm/api/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /broker/shoonya/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /broker/tradejini/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /broker/upstox/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /broker/zebu/api/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /broker/aliceblue/api/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /broker/dhan_sandbox/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /broker/firstock/api/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /broker/fivepaisa/api/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /broker/flattrade/api/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /broker/ibulls/api/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /broker/indmoney/api/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /broker/jainamxts/api/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /broker/motilal/api/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /broker/mstock/api/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /broker/pocketful/api/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /broker/samco/mapping/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /broker/shoonya/api/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /broker/tradejini/api/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /broker/upstox/api/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /broker/wisdom/api/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /broker/zerodha/api/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /download/duckdb_downloader.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /log/readme.md: -------------------------------------------------------------------------------- 1 | This is a Log Folder -------------------------------------------------------------------------------- /broker/compositedge/api/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /broker/dhan_sandbox/api/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /broker/fivepaisaxts/api/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /collections/openalgo/collection.bru: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /examples/go/readme.md: -------------------------------------------------------------------------------- 1 | OpenAlgo Go SDK Examples -------------------------------------------------------------------------------- /broker/samco/__init__.py: -------------------------------------------------------------------------------- 1 | # Samco broker module 2 | -------------------------------------------------------------------------------- /broker/samco/api/__init__.py: -------------------------------------------------------------------------------- 1 | # Samco API module 2 | -------------------------------------------------------------------------------- /db/readme.txt: -------------------------------------------------------------------------------- 1 | This Folder Contains Sqlite DB Files -------------------------------------------------------------------------------- /examples/nodejs/readme.md: -------------------------------------------------------------------------------- 1 | OpenAlgo Nodejs SDK Examples -------------------------------------------------------------------------------- /broker/iifl/streaming/__init__.py: -------------------------------------------------------------------------------- 1 | # Iifl streaming module -------------------------------------------------------------------------------- /broker/ibulls/streaming/__init__.py: -------------------------------------------------------------------------------- 1 | # Ibulls streaming module -------------------------------------------------------------------------------- /broker/samco/database/__init__.py: -------------------------------------------------------------------------------- 1 | # Samco database module 2 | -------------------------------------------------------------------------------- /broker/wisdom/streaming/__init__.py: -------------------------------------------------------------------------------- 1 | # Wisdom streaming module -------------------------------------------------------------------------------- /broker/jainamxts/streaming/__init__.py: -------------------------------------------------------------------------------- 1 | # JainamXTS streaming module -------------------------------------------------------------------------------- /tmp/README.md: -------------------------------------------------------------------------------- 1 | this is a temp file to process the master contracts -------------------------------------------------------------------------------- /broker/compositedge/streaming/__init__.py: -------------------------------------------------------------------------------- 1 | # Compositedge streaming module -------------------------------------------------------------------------------- /broker/definedge/api/__init__.py: -------------------------------------------------------------------------------- 1 | # DefinedGe Securities API modules 2 | -------------------------------------------------------------------------------- /broker/fivepaisaxts/streaming/__init__.py: -------------------------------------------------------------------------------- 1 | # FivepaisaXTS streaming module -------------------------------------------------------------------------------- /broker/fyers/streaming/__init__.py: -------------------------------------------------------------------------------- 1 | # Fyers HSM WebSocket Streaming Module -------------------------------------------------------------------------------- /broker/definedge/database/__init__.py: -------------------------------------------------------------------------------- 1 | # DefinedGe Securities database modules 2 | -------------------------------------------------------------------------------- /broker/definedge/mapping/__init__.py: -------------------------------------------------------------------------------- 1 | # DefinedGe Securities mapping modules 2 | -------------------------------------------------------------------------------- /broker/definedge/streaming/__init__.py: -------------------------------------------------------------------------------- 1 | # DefinedGe Securities streaming modules 2 | -------------------------------------------------------------------------------- /download/symbols.csv: -------------------------------------------------------------------------------- 1 | RELIANCE 2 | ICICIBANK 3 | HDFCBANK 4 | SBIN 5 | TCS 6 | INFY -------------------------------------------------------------------------------- /broker/definedge/__init__.py: -------------------------------------------------------------------------------- 1 | # DefinedGe Securities Broker Integration for OpenAlgo 2 | -------------------------------------------------------------------------------- /test/sandbox/__init__.py: -------------------------------------------------------------------------------- 1 | # test/sandbox/__init__.py 2 | """Sandbox test suite""" 3 | -------------------------------------------------------------------------------- /static/favicon/logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/marketcalls/openalgo/HEAD/static/favicon/logo.png -------------------------------------------------------------------------------- /static/images/image.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/marketcalls/openalgo/HEAD/static/images/image.png -------------------------------------------------------------------------------- /static/images/yoga.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/marketcalls/openalgo/HEAD/static/images/yoga.png -------------------------------------------------------------------------------- /static/sounds/alert.mp3: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/marketcalls/openalgo/HEAD/static/sounds/alert.mp3 -------------------------------------------------------------------------------- /static/favicon/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/marketcalls/openalgo/HEAD/static/favicon/favicon.ico -------------------------------------------------------------------------------- /static/images/copy-icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/marketcalls/openalgo/HEAD/static/images/copy-icon.png -------------------------------------------------------------------------------- /static/images/thumbs-up.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/marketcalls/openalgo/HEAD/static/images/thumbs-up.png -------------------------------------------------------------------------------- /docs/architecture-diagram.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/marketcalls/openalgo/HEAD/docs/architecture-diagram.png -------------------------------------------------------------------------------- /static/images/brokers/angel.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/marketcalls/openalgo/HEAD/static/images/brokers/angel.png -------------------------------------------------------------------------------- /static/images/brokers/dhan.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/marketcalls/openalgo/HEAD/static/images/brokers/dhan.png -------------------------------------------------------------------------------- /static/images/brokers/fyers.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/marketcalls/openalgo/HEAD/static/images/brokers/fyers.png -------------------------------------------------------------------------------- /static/images/brokers/icici.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/marketcalls/openalgo/HEAD/static/images/brokers/icici.png -------------------------------------------------------------------------------- /static/images/brokers/kotak.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/marketcalls/openalgo/HEAD/static/images/brokers/kotak.png -------------------------------------------------------------------------------- /static/favicon/favicon-16x16.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/marketcalls/openalgo/HEAD/static/favicon/favicon-16x16.png -------------------------------------------------------------------------------- /static/favicon/favicon-32x32.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/marketcalls/openalgo/HEAD/static/favicon/favicon-32x32.png -------------------------------------------------------------------------------- /static/favicon/mstile-150x150.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/marketcalls/openalgo/HEAD/static/favicon/mstile-150x150.png -------------------------------------------------------------------------------- /static/images/brokers/5paisa.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/marketcalls/openalgo/HEAD/static/images/brokers/5paisa.png -------------------------------------------------------------------------------- /static/images/brokers/upstox.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/marketcalls/openalgo/HEAD/static/images/brokers/upstox.png -------------------------------------------------------------------------------- /static/images/brokers/zerodha.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/marketcalls/openalgo/HEAD/static/images/brokers/zerodha.png -------------------------------------------------------------------------------- /playground/Playground-screenshot.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/marketcalls/openalgo/HEAD/playground/Playground-screenshot.png -------------------------------------------------------------------------------- /static/favicon/apple-touch-icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/marketcalls/openalgo/HEAD/static/favicon/apple-touch-icon.png -------------------------------------------------------------------------------- /static/images/brokers/aliceblue.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/marketcalls/openalgo/HEAD/static/images/brokers/aliceblue.png -------------------------------------------------------------------------------- /static/favicon/android-chrome-192x192.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/marketcalls/openalgo/HEAD/static/favicon/android-chrome-192x192.png -------------------------------------------------------------------------------- /collections/openalgo/bruno.json: -------------------------------------------------------------------------------- 1 | { 2 | "version": "1", 3 | "name": "openalgo", 4 | "type": "collection", 5 | "ignore": [ 6 | "node_modules", 7 | ".git" 8 | ] 9 | } -------------------------------------------------------------------------------- /broker/dhan_sandbox/streaming/__init__.py: -------------------------------------------------------------------------------- 1 | """ 2 | Dhan WebSocket streaming integration for OpenAlgo. 3 | """ 4 | from .dhan_adapter import DhanWebSocketAdapter 5 | 6 | __all__ = ["DhanWebSocketAdapter"] 7 | -------------------------------------------------------------------------------- /download/.sample.env: -------------------------------------------------------------------------------- 1 | API_KEY=your_openalgo_api_key 2 | DB_NAME=amibroker.db 3 | INTERVAL=1m 4 | HOST=http://127.0.0.1:5000 5 | MAX_REQUESTS_PER_SECOND=10 6 | POLLING_INTERVAL_SECONDS=5 7 | INITIAL_DAYS=30 8 | -------------------------------------------------------------------------------- /log/strategies/.gitignore: -------------------------------------------------------------------------------- 1 | # Ignore all strategy log files 2 | *.log 3 | 4 | # Keep this .gitignore file 5 | !.gitignore 6 | 7 | # This ensures the log/strategies folder exists in git but log files are not tracked -------------------------------------------------------------------------------- /keys/.gitignore: -------------------------------------------------------------------------------- 1 | # Ignore all files in this directory 2 | * 3 | 4 | # Except this .gitignore file 5 | !.gitignore 6 | 7 | # This directory contains sensitive encryption keys 8 | # NEVER commit these files to version control -------------------------------------------------------------------------------- /postcss.config.mjs: -------------------------------------------------------------------------------- 1 | export default { 2 | plugins: { 3 | '@tailwindcss/postcss': {}, 4 | autoprefixer: {}, 5 | ...(process.env.NODE_ENV === 'production' ? { cssnano: { preset: 'default' } } : {}) 6 | } 7 | } 8 | -------------------------------------------------------------------------------- /database/tv_search.py: -------------------------------------------------------------------------------- 1 | # database/tv_search.py 2 | 3 | from database.symbol import SymToken 4 | 5 | 6 | def search_symbols(symbol,exchange): 7 | return SymToken.query.filter(SymToken.symbol == symbol,SymToken.exchange == exchange).all() 8 | -------------------------------------------------------------------------------- /templates/components/loading_spinner.html: -------------------------------------------------------------------------------- 1 | 6 | -------------------------------------------------------------------------------- /utils/version.py: -------------------------------------------------------------------------------- 1 | # OpenAlgo Version Management 2 | # This file is the single source of truth for version information 3 | 4 | VERSION = '1.0.0.39' 5 | 6 | def get_version(): 7 | """Return the current OpenAlgo version""" 8 | return VERSION 9 | -------------------------------------------------------------------------------- /broker/ibulls/plugin.json: -------------------------------------------------------------------------------- 1 | { 2 | "Plugin Name": "IBulls", 3 | "Plugin URI": "https://openalgo.in", 4 | "Description":"IBulls Plugin", 5 | "Version": "1.0", 6 | "Author": "Kalaivani", 7 | "Author URI": "https://openalgo.in" 8 | } 9 | -------------------------------------------------------------------------------- /broker/iifl/plugin.json: -------------------------------------------------------------------------------- 1 | { 2 | "Plugin Name": "IIFL", 3 | "Plugin URI": "https://openalgo.in", 4 | "Description":"IIFL OpenAlgo Plugin", 5 | "Version": "1.0", 6 | "Author": "Kalaivani", 7 | "Author URI": "https://openalgo.in" 8 | } 9 | -------------------------------------------------------------------------------- /broker/zebu/plugin.json: -------------------------------------------------------------------------------- 1 | { 2 | "Plugin Name": "Zebu", 3 | "Plugin URI": "https://openalgo.in", 4 | "Description": "Zebu OpenAlgo Plugin", 5 | "Version": "1.0", 6 | "Author": "Rajandran R", 7 | "Author URI": "https://openalgo.in" 8 | } -------------------------------------------------------------------------------- /broker/angel/plugin.json: -------------------------------------------------------------------------------- 1 | { 2 | "Plugin Name": "angel", 3 | "Plugin URI": "https://openalgo.in", 4 | "Description": "AngelOne OpenAlgo Plugin", 5 | "Version": "1.0", 6 | "Author": "Rajandran R", 7 | "Author URI": "https://openalgo.in" 8 | } -------------------------------------------------------------------------------- /broker/dhan/plugin.json: -------------------------------------------------------------------------------- 1 | { 2 | "Plugin Name": "dhan", 3 | "Plugin URI": "https://openalgo.in", 4 | "Description": "Dhan OpenAlgo Plugin", 5 | "Version": "1.0", 6 | "Author": "Rajandran R", 7 | "Author URI": "https://openalgo.in" 8 | } 9 | -------------------------------------------------------------------------------- /broker/groww/plugin.json: -------------------------------------------------------------------------------- 1 | { 2 | "Plugin Name": "groww", 3 | "Plugin URI": "https://openalgo.in", 4 | "Description": "Groww OpenAlgo Plugin", 5 | "Version": "1.0", 6 | "Author": "Kalaivani", 7 | "Author URI": "https://openalgo.in" 8 | } 9 | -------------------------------------------------------------------------------- /broker/mstock/plugin.json: -------------------------------------------------------------------------------- 1 | { 2 | "Plugin Name": "mstock", 3 | "Plugin URI": "https://openalgo.in", 4 | "Description": "mstock OpenAlgo Plugin", 5 | "Version": "1.0", 6 | "Author": "Rajandran R", 7 | "Author URI": "https://openalgo.in" 8 | } -------------------------------------------------------------------------------- /broker/paytm/plugin.json: -------------------------------------------------------------------------------- 1 | { 2 | "Plugin Name": "paytm", 3 | "Plugin URI": "https://openalgo.in", 4 | "Description": "Paytm OpenAlgo Plugin", 5 | "Version": "1.0", 6 | "Author": "Naidu A", 7 | "Author URI": "https://openalgo.in" 8 | } 9 | -------------------------------------------------------------------------------- /broker/firstock/plugin.json: -------------------------------------------------------------------------------- 1 | { 2 | "Plugin Name": "Firstock", 3 | "Plugin URI": "https://openalgo.in", 4 | "Description": "Firstock OpenAlgo Plugin", 5 | "Version": "1.0", 6 | "Author": "Rajandran R", 7 | "Author URI": "https://openalgo.in" 8 | } -------------------------------------------------------------------------------- /broker/fivepaisa/plugin.json: -------------------------------------------------------------------------------- 1 | { 2 | "Plugin Name": "fivepaisa", 3 | "Plugin URI": "https://openalgo.in", 4 | "Description": "5paisa OpenAlgo Plugin", 5 | "Version": "1.0", 6 | "Author": "Rajandran R", 7 | "Author URI": "https://openalgo.in" 8 | } -------------------------------------------------------------------------------- /broker/fyers/plugin.json: -------------------------------------------------------------------------------- 1 | { 2 | "Plugin Name": "fyers", 3 | "Plugin URI": "https://openalgo.in", 4 | "Description": "Fyers OpenAlgo Plugin", 5 | "Version": "1.0", 6 | "Author": "Rajandran R", 7 | "Author URI": "https://openalgo.in" 8 | } 9 | -------------------------------------------------------------------------------- /broker/shoonya/plugin.json: -------------------------------------------------------------------------------- 1 | { 2 | "Plugin Name": "Shoonya", 3 | "Plugin URI": "https://openalgo.in", 4 | "Description": "Shoonya OpenAlgo Plugin", 5 | "Version": "1.0", 6 | "Author": "Rajandran R", 7 | "Author URI": "https://openalgo.in" 8 | } -------------------------------------------------------------------------------- /broker/upstox/plugin.json: -------------------------------------------------------------------------------- 1 | { 2 | "Plugin Name": "upstox", 3 | "Plugin URI": "https://openalgo.in", 4 | "Description": "Upstox OpenAlgo Plugin", 5 | "Version": "1.0", 6 | "Author": "Rajandran R", 7 | "Author URI": "https://openalgo.in" 8 | } 9 | -------------------------------------------------------------------------------- /broker/dhan_sandbox/plugin.json: -------------------------------------------------------------------------------- 1 | { 2 | "Plugin Name": "dhan", 3 | "Plugin URI": "https://openalgo.in", 4 | "Description": "Dhan OpenAlgo Plugin", 5 | "Version": "1.0", 6 | "Author": "Rajandran R", 7 | "Author URI": "https://openalgo.in" 8 | } 9 | -------------------------------------------------------------------------------- /broker/fivepaisaxts/plugin.json: -------------------------------------------------------------------------------- 1 | { 2 | "Plugin Name": "5paisa (XTS)", 3 | "Plugin URI": "https://openalgo.in", 4 | "Description":"5Paisa XTS Plugin", 5 | "Version": "1.0", 6 | "Author": "Kalaivani", 7 | "Author URI": "https://openalgo.in" 8 | } 9 | -------------------------------------------------------------------------------- /broker/flattrade/plugin.json: -------------------------------------------------------------------------------- 1 | { 2 | "Plugin Name": "Flattrade", 3 | "Plugin URI": "https://openalgo.in", 4 | "Description": "Flattrade OpenAlgo Plugin", 5 | "Version": "1.0", 6 | "Author": "Rajandran R", 7 | "Author URI": "https://openalgo.in" 8 | } -------------------------------------------------------------------------------- /broker/kotak/plugin.json: -------------------------------------------------------------------------------- 1 | { 2 | "Plugin Name": "Kotak", 3 | "Plugin URI": "https://openalgo.in", 4 | "Description": "Kotak OpenAlgo Plugin", 5 | "Version": "1.0", 6 | "Author": "K Siva Rama Krishna Reddy", 7 | "Author URI": "https://openalgo.in" 8 | } -------------------------------------------------------------------------------- /broker/tradejini/plugin.json: -------------------------------------------------------------------------------- 1 | { 2 | "Plugin Name": "Flattrade", 3 | "Plugin URI": "https://openalgo.in", 4 | "Description": "Flattrade OpenAlgo Plugin", 5 | "Version": "1.0", 6 | "Author": "Rajandran R", 7 | "Author URI": "https://openalgo.in" 8 | } -------------------------------------------------------------------------------- /broker/zerodha/plugin.json: -------------------------------------------------------------------------------- 1 | { 2 | "Plugin Name": "zerodha", 3 | "Plugin URI": "https://openalgo.in", 4 | "Description": "Zerodha OpenAlgo Plugin", 5 | "Version": "1.0", 6 | "Author": "Rajandran R", 7 | "Author URI": "https://openalgo.in" 8 | } 9 | -------------------------------------------------------------------------------- /broker/indmoney/plugin.json: -------------------------------------------------------------------------------- 1 | { 2 | "Plugin Name": "indmoney", 3 | "Plugin URI": "https://openalgo.in", 4 | "Description": "IndMoney OpenAlgo Plugin", 5 | "Version": "1.0", 6 | "Author": "Deepanshu Goyal", 7 | "Author URI": "https://openalgo.in" 8 | } 9 | -------------------------------------------------------------------------------- /broker/jainamxts/plugin.json: -------------------------------------------------------------------------------- 1 | { 2 | "Plugin Name": "jainamxts", 3 | "Plugin URI": "https://openalgo.in", 4 | "Description":"CompositedgeOpenAlgo Plugin", 5 | "Version": "1.0", 6 | "Author": "Kalaivani", 7 | "Author URI": "https://openalgo.in" 8 | } 9 | -------------------------------------------------------------------------------- /broker/motilal/plugin.json: -------------------------------------------------------------------------------- 1 | { 2 | "Plugin Name": "motilal", 3 | "Plugin URI": "https://openalgo.in", 4 | "Description": "Motilal Oswal OpenAlgo Plugin", 5 | "Version": "1.0", 6 | "Author": "Deepanshu Goyal", 7 | "Author URI": "https://openalgo.in" 8 | } -------------------------------------------------------------------------------- /broker/aliceblue/plugin.json: -------------------------------------------------------------------------------- 1 | { 2 | "Plugin Name": "aliceblue", 3 | "Plugin URI": "https://openalgo.in", 4 | "Description": "AliceBlue OpenAlgo Plugin", 5 | "Version": "1.0", 6 | "Author": "Deepanshu Goyal", 7 | "Author URI": "https://openalgo.in" 8 | } 9 | -------------------------------------------------------------------------------- /broker/compositedge/plugin.json: -------------------------------------------------------------------------------- 1 | { 2 | "Plugin Name": "compositedge", 3 | "Plugin URI": "https://openalgo.in", 4 | "Description":"CompositedgeOpenAlgo Plugin", 5 | "Version": "1.0", 6 | "Author": "Kalaivani", 7 | "Author URI": "https://openalgo.in" 8 | } 9 | -------------------------------------------------------------------------------- /broker/pocketful/plugin.json: -------------------------------------------------------------------------------- 1 | { 2 | "Plugin Name": "pocketful", 3 | "Plugin URI": "https://openalgo.in", 4 | "Description": "Pocketful OpenAlgo Plugin", 5 | "Version": "1.0", 6 | "Author": "Deepanshu Goyal", 7 | "Author URI": "https://openalgo.in" 8 | } 9 | -------------------------------------------------------------------------------- /broker/wisdom/plugin.json: -------------------------------------------------------------------------------- 1 | { 2 | "Plugin Name": "Wisdom Capital (XTS)", 3 | "Plugin URI": "https://openalgo.in", 4 | "Description":"Wisdom Capital XTS Plugin", 5 | "Version": "1.0", 6 | "Author": "Kalaivani", 7 | "Author URI": "https://openalgo.in" 8 | } 9 | -------------------------------------------------------------------------------- /collections/openalgo/ping.bru: -------------------------------------------------------------------------------- 1 | meta { 2 | name: Ping 3 | type: http 4 | seq: 22 5 | } 6 | 7 | post { 8 | url: http://127.0.0.1:5000/api/v1/ping 9 | body: json 10 | auth: none 11 | } 12 | 13 | body:json { 14 | { 15 | "apikey": "" 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /collections/openalgo/ticker.bru: -------------------------------------------------------------------------------- 1 | meta { 2 | name: Ticker 3 | type: http 4 | seq: 16 5 | } 6 | 7 | get { 8 | url: http://127.0.0.1:5000/api/v1/ticker/NSE:SBIN?apikey=&interval=D&from=2025-01-01&to=2025-01-31&format=json 9 | body: none 10 | auth: none 11 | } 12 | -------------------------------------------------------------------------------- /broker/firstock/streaming/__init__.py: -------------------------------------------------------------------------------- 1 | from .firstock_adapter import FirstockWebSocketAdapter 2 | from .firstock_mapping import FirstockExchangeMapper 3 | from .firstock_websocket import FirstockWebSocket 4 | 5 | __all__ = ['FirstockWebSocketAdapter', 'FirstockExchangeMapper', 'FirstockWebSocket'] -------------------------------------------------------------------------------- /broker/definedge/plugin.json: -------------------------------------------------------------------------------- 1 | { 2 | "Plugin Name": "DefinedGe Securities", 3 | "Plugin URI": "https://openalgo.in", 4 | "Description": "DefinedGe Securities OpenAlgo Plugin", 5 | "Version": "1.0", 6 | "Author": "OpenAlgo Team", 7 | "Author URI": "https://openalgo.in" 8 | } 9 | -------------------------------------------------------------------------------- /broker/iifl/baseurl.py: -------------------------------------------------------------------------------- 1 | """IIFL broker base URLs configuration.""" 2 | 3 | # Base URL for IIFL API endpoints 4 | BASE_URL = "https://ttblaze.iifl.com" 5 | 6 | # Derived URLs for specific API endpoints 7 | MARKET_DATA_URL = f"{BASE_URL}/apimarketdata" 8 | INTERACTIVE_URL = f"{BASE_URL}/interactive" 9 | -------------------------------------------------------------------------------- /collections/openalgo/chart_get.bru: -------------------------------------------------------------------------------- 1 | meta { 2 | name: chart_get 3 | type: http 4 | seq: 41 5 | } 6 | 7 | get { 8 | url: http://127.0.0.1:5000/api/v1/chart?apikey=a85992a13ab7db424c239c50826116366e9f4fd8c591345a2d23aad01ffa4d00 9 | body: none 10 | auth: none 11 | } 12 | -------------------------------------------------------------------------------- /tailwind.config.mjs: -------------------------------------------------------------------------------- 1 | import daisyui from 'daisyui'; 2 | 3 | /** @type {import('tailwindcss').Config} */ 4 | export default { 5 | content: [ 6 | "./templates/**/*.html", 7 | "./static/**/*.js", 8 | ], 9 | theme: { 10 | extend: {} 11 | }, 12 | plugins: [daisyui] 13 | } 14 | -------------------------------------------------------------------------------- /collections/openalgo/analyzer_status.bru: -------------------------------------------------------------------------------- 1 | meta { 2 | name: Analyzer Status 3 | type: http 4 | seq: 1 5 | } 6 | 7 | post { 8 | url: http://127.0.0.1:5000/api/v1/analyzer 9 | body: json 10 | auth: none 11 | } 12 | 13 | body:json { 14 | { 15 | "apikey": "" 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /broker/wisdom/baseurl.py: -------------------------------------------------------------------------------- 1 | """Wisdom broker base URLs configuration.""" 2 | 3 | # Base URL for Wisdom API endpoints 4 | BASE_URL = "https://trade.wisdomcapital.in" 5 | 6 | # Derived URLs for specific API endpoints 7 | MARKET_DATA_URL = f"{BASE_URL}/apimarketdata" 8 | INTERACTIVE_URL = f"{BASE_URL}/interactive" 9 | -------------------------------------------------------------------------------- /static/favicon/browserconfig.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | #da532c 7 | 8 | 9 | 10 | -------------------------------------------------------------------------------- /broker/compositedge/baseurl.py: -------------------------------------------------------------------------------- 1 | """CompositEdge broker base URLs configuration.""" 2 | 3 | # Base URL for CompositEdge API endpoints 4 | BASE_URL = "https://xts.compositedge.com" 5 | 6 | # Derived URLs for specific API endpoints 7 | MARKET_DATA_URL = f"{BASE_URL}/apimarketdata" 8 | INTERACTIVE_URL = f"{BASE_URL}/interactive" 9 | -------------------------------------------------------------------------------- /broker/fivepaisaxts/baseurl.py: -------------------------------------------------------------------------------- 1 | """FivepaisaXTS broker base URLs configuration.""" 2 | 3 | # Base URL for FivepaisaXTS API endpoints 4 | BASE_URL = "https://xtsmum.5paisa.com/" 5 | 6 | # Derived URLs for specific API endpoints 7 | MARKET_DATA_URL = f"{BASE_URL}/apimarketdata" 8 | INTERACTIVE_URL = f"{BASE_URL}/interactive" 9 | -------------------------------------------------------------------------------- /broker/jainamxts/baseurl.py: -------------------------------------------------------------------------------- 1 | """Jainamxts broker base URLs configuration.""" 2 | 3 | # Base URL for Jainamxts API endpoints 4 | BASE_URL = "https://jtrade.jainam.in:5000" 5 | 6 | # Derived URLs for specific API endpoints 7 | MARKET_DATA_URL = f"{BASE_URL}/apibinarymarketdata" 8 | INTERACTIVE_URL = f"{BASE_URL}/interactive" 9 | -------------------------------------------------------------------------------- /broker/kotak/streaming/__init__.py: -------------------------------------------------------------------------------- 1 | """ 2 | Kotak WebSocket streaming integration for OpenAlgo. 3 | Exposes the high-level adapter and core websocket client. 4 | """ 5 | from .kotak_adapter import KotakWebSocketAdapter 6 | from .kotak_websocket import KotakWebSocket 7 | 8 | __all__ = ["KotakWebSocketAdapter", "KotakWebSocket"] 9 | -------------------------------------------------------------------------------- /broker/ibulls/baseurl.py: -------------------------------------------------------------------------------- 1 | """CompositEdge broker base URLs configuration.""" 2 | 3 | # Base URL for CompositEdge API endpoints 4 | BASE_URL = "https://xts.ibullssecurities.com" 5 | 6 | # Derived URLs for specific API endpoints 7 | MARKET_DATA_URL = f"{BASE_URL}/apibinarymarketdata" 8 | INTERACTIVE_URL = f"{BASE_URL}/interactive" 9 | -------------------------------------------------------------------------------- /collections/openalgo/analyzer_toggle.bru: -------------------------------------------------------------------------------- 1 | meta { 2 | name: Analyzer Toggle 3 | type: http 4 | seq: 2 5 | } 6 | 7 | post { 8 | url: http://127.0.0.1:5000/api/v1/analyzer/toggle 9 | body: json 10 | auth: none 11 | } 12 | 13 | body:json { 14 | { 15 | "apikey": "", 16 | "mode": true 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /collections/openalgo/funds.bru: -------------------------------------------------------------------------------- 1 | meta { 2 | name: funds 3 | type: http 4 | seq: 12 5 | } 6 | 7 | post { 8 | url: http://127.0.0.1:5000/api/v1/funds 9 | body: json 10 | auth: none 11 | } 12 | 13 | body:json { 14 | { 15 | "apikey": "a85992a13ab7db424c239c50826116366e9f4fd8c591345a2d23aad01ffa4d00" 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /collections/openalgo/symbol.bru: -------------------------------------------------------------------------------- 1 | meta { 2 | name: Symbol 3 | type: http 4 | seq: 10 5 | } 6 | 7 | post { 8 | url: http://127.0.0.1:5000/api/v1/symbol 9 | body: json 10 | auth: none 11 | } 12 | 13 | body:json { 14 | { 15 | "apikey": "", 16 | "symbol": "SBIN", 17 | "exchange": "NSE" 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /limiter.py: -------------------------------------------------------------------------------- 1 | # limiter.py 2 | 3 | from flask_limiter import Limiter 4 | from flask_limiter.util import get_remote_address 5 | 6 | # Initialize Flask-Limiter without the app object 7 | limiter = Limiter( 8 | key_func=get_remote_address, 9 | storage_uri="memory://", 10 | strategy="moving-window" 11 | ) 12 | -------------------------------------------------------------------------------- /collections/openalgo/search.bru: -------------------------------------------------------------------------------- 1 | meta { 2 | name: Search 3 | type: http 4 | seq: 11 5 | } 6 | 7 | post { 8 | url: http://127.0.0.1:5000/api/v1/search 9 | body: json 10 | auth: none 11 | } 12 | 13 | body:json { 14 | { 15 | "apikey": "", 16 | "query": "NIFTY 25000 CE", 17 | "exchange": "NFO" 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /collections/openalgo/holdings.bru: -------------------------------------------------------------------------------- 1 | meta { 2 | name: holdings 3 | type: http 4 | seq: 16 5 | } 6 | 7 | post { 8 | url: http://127.0.0.1:5000/api/v1/holdings 9 | body: json 10 | auth: none 11 | } 12 | 13 | body:json { 14 | { 15 | "apikey": "a85992a13ab7db424c239c50826116366e9f4fd8c591345a2d23aad01ffa4d00" 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /collections/openalgo/orderbook.bru: -------------------------------------------------------------------------------- 1 | meta { 2 | name: orderbook 3 | type: http 4 | seq: 13 5 | } 6 | 7 | post { 8 | url: http://127.0.0.1:5000/api/v1/orderbook 9 | body: json 10 | auth: none 11 | } 12 | 13 | body:json { 14 | { 15 | "apikey": "a85992a13ab7db424c239c50826116366e9f4fd8c591345a2d23aad01ffa4d00" 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /collections/openalgo/tradebook.bru: -------------------------------------------------------------------------------- 1 | meta { 2 | name: tradebook 3 | type: http 4 | seq: 14 5 | } 6 | 7 | post { 8 | url: http://127.0.0.1:5000/api/v1/tradebook 9 | body: json 10 | auth: none 11 | } 12 | 13 | body:json { 14 | { 15 | "apikey": "a85992a13ab7db424c239c50826116366e9f4fd8c591345a2d23aad01ffa4d00" 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /broker/groww/streaming/__init__.py: -------------------------------------------------------------------------------- 1 | """ 2 | Groww WebSocket streaming module for OpenAlgo 3 | """ 4 | 5 | from .groww_adapter import GrowwWebSocketAdapter 6 | from .groww_mapping import GrowwExchangeMapper, GrowwCapabilityRegistry 7 | 8 | __all__ = [ 9 | 'GrowwWebSocketAdapter', 10 | 'GrowwExchangeMapper', 11 | 'GrowwCapabilityRegistry' 12 | ] -------------------------------------------------------------------------------- /collections/openalgo/instruments.bru: -------------------------------------------------------------------------------- 1 | meta { 2 | name: Instruments 3 | type: http 4 | seq: 17 5 | } 6 | 7 | get { 8 | url: http://127.0.0.1:5000/api/v1/instruments 9 | body: json 10 | auth: none 11 | } 12 | 13 | body:json { 14 | { 15 | "apikey": "", 16 | "exchange": "NSE_INDEX", 17 | "format": "json" 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /collections/openalgo/intervals.bru: -------------------------------------------------------------------------------- 1 | meta { 2 | name: intervals 3 | type: http 4 | seq: 11 5 | } 6 | 7 | post { 8 | url: http://127.0.0.1:5000/api/v1/intervals 9 | body: json 10 | auth: none 11 | } 12 | 13 | body:json { 14 | { 15 | "apikey": "a85992a13ab7db424c239c50826116366e9f4fd8c591345a2d23aad01ffa4d00" 16 | 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /collections/openalgo/orderstatus.bru: -------------------------------------------------------------------------------- 1 | meta { 2 | name: Order Status 3 | type: http 4 | seq: 14 5 | } 6 | 7 | post { 8 | url: http://127.0.0.1:5000/api/v1/orderstatus 9 | body: json 10 | auth: none 11 | } 12 | 13 | body:json { 14 | { 15 | "apikey": "", 16 | "strategy": "Test Strategy", 17 | "orderid": "" 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /collections/openalgo/positionbook.bru: -------------------------------------------------------------------------------- 1 | meta { 2 | name: positionbook 3 | type: http 4 | seq: 15 5 | } 6 | 7 | post { 8 | url: http://127.0.0.1:5000/api/v1/positionbook 9 | body: json 10 | auth: none 11 | } 12 | 13 | body:json { 14 | { 15 | "apikey": "a85992a13ab7db424c239c50826116366e9f4fd8c591345a2d23aad01ffa4d00" 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /broker/samco/streaming/__init__.py: -------------------------------------------------------------------------------- 1 | from .samco_adapter import SamcoWebSocketAdapter 2 | from .samcoWebSocket import SamcoWebSocket 3 | from .samco_mapping import SamcoExchangeMapper, SamcoCapabilityRegistry 4 | 5 | __all__ = [ 6 | 'SamcoWebSocketAdapter', 7 | 'SamcoWebSocket', 8 | 'SamcoExchangeMapper', 9 | 'SamcoCapabilityRegistry' 10 | ] 11 | -------------------------------------------------------------------------------- /broker/zerodha/streaming/__init__.py: -------------------------------------------------------------------------------- 1 | """ 2 | Zerodha WebSocket streaming module for OpenAlgo. 3 | 4 | This module provides WebSocket integration with Zerodha's market data streaming API, 5 | following the OpenAlgo WebSocket proxy architecture. 6 | """ 7 | 8 | from .zerodha_adapter import ZerodhaWebSocketAdapter 9 | 10 | __all__ = ['ZerodhaWebSocketAdapter'] 11 | -------------------------------------------------------------------------------- /collections/openalgo/optiongreeks.bru: -------------------------------------------------------------------------------- 1 | meta { 2 | name: Option Greeks 3 | type: http 4 | seq: 15 5 | } 6 | 7 | post { 8 | url: http://127.0.0.1:5000/api/v1/optiongreeks 9 | body: json 10 | auth: none 11 | } 12 | 13 | body:json { 14 | { 15 | "apikey": "", 16 | "exchange": "NFO", 17 | "symbol": "NIFTY30DEC2526000CE" 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /collections/openalgo/expiry.bru: -------------------------------------------------------------------------------- 1 | meta { 2 | name: Expiry 3 | type: http 4 | seq: 13 5 | } 6 | 7 | post { 8 | url: http://127.0.0.1:5000/api/v1/expiry 9 | body: json 10 | auth: none 11 | } 12 | 13 | body:json { 14 | { 15 | "apikey": "", 16 | "symbol": "NIFTY", 17 | "exchange": "NFO", 18 | "instrumenttype": "options" 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /broker/tradejini/streaming/__init__.py: -------------------------------------------------------------------------------- 1 | """ 2 | Tradejini WebSocket streaming module 3 | """ 4 | 5 | from .tradejini_adapter import TradejiniWebSocketAdapter 6 | from .tradejini_mapping import TradejiniExchangeMapper, TradejiniCapabilityRegistry 7 | 8 | __all__ = [ 9 | 'TradejiniWebSocketAdapter', 10 | 'TradejiniExchangeMapper', 11 | 'TradejiniCapabilityRegistry' 12 | ] -------------------------------------------------------------------------------- /static/favicon/site.webmanifest: -------------------------------------------------------------------------------- 1 | { 2 | "name": "", 3 | "short_name": "", 4 | "icons": [ 5 | { 6 | "src": "/android-chrome-192x192.png", 7 | "sizes": "192x192", 8 | "type": "image/png" 9 | } 10 | ], 11 | "theme_color": "#ffffff", 12 | "background_color": "#ffffff", 13 | "display": "standalone" 14 | } 15 | -------------------------------------------------------------------------------- /broker/paytm/streaming/__init__.py: -------------------------------------------------------------------------------- 1 | # Paytm streaming module 2 | from .paytm_adapter import PaytmWebSocketAdapter 3 | from .paytm_mapping import PaytmExchangeMapper, PaytmCapabilityRegistry 4 | from .paytm_websocket import PaytmWebSocket 5 | 6 | __all__ = [ 7 | 'PaytmWebSocketAdapter', 8 | 'PaytmExchangeMapper', 9 | 'PaytmCapabilityRegistry', 10 | 'PaytmWebSocket' 11 | ] 12 | -------------------------------------------------------------------------------- /broker/dhan/streaming/__init__.py: -------------------------------------------------------------------------------- 1 | """ 2 | Dhan WebSocket streaming module 3 | """ 4 | from .dhan_adapter import DhanWebSocketAdapter 5 | from .dhan_mapping import DhanExchangeMapper, DhanCapabilityRegistry 6 | from .dhan_websocket import DhanWebSocket 7 | 8 | __all__ = [ 9 | 'DhanWebSocketAdapter', 10 | 'DhanExchangeMapper', 11 | 'DhanCapabilityRegistry', 12 | 'DhanWebSocket' 13 | ] -------------------------------------------------------------------------------- /collections/openalgo/DATA/optionchain.bru: -------------------------------------------------------------------------------- 1 | meta { 2 | name: Option Chain 3 | type: http 4 | seq: 15 5 | } 6 | 7 | post { 8 | url: http://127.0.0.1:5000/api/v1/optionchain 9 | body: json 10 | auth: none 11 | } 12 | 13 | body:json { 14 | { 15 | "apikey": "", 16 | "underlying": "NIFTY", 17 | "exchange": "NSE_INDEX", 18 | "expiry_date": "30DEC25" 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /collections/openalgo/quotes.bru: -------------------------------------------------------------------------------- 1 | meta { 2 | name: quotes 3 | type: http 4 | seq: 8 5 | } 6 | 7 | post { 8 | url: http://127.0.0.1:5000/api/v1/quotes 9 | body: json 10 | auth: none 11 | } 12 | 13 | body:json { 14 | { 15 | "apikey": "a85992a13ab7db424c239c50826116366e9f4fd8c591345a2d23aad01ffa4d00", 16 | "symbol":"SBIN", 17 | "exchange":"NSE" 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /collections/openalgo/depth.bru: -------------------------------------------------------------------------------- 1 | meta { 2 | name: depth 3 | type: http 4 | seq: 10 5 | } 6 | 7 | post { 8 | url: http://127.0.0.1:5000/api/v1/depth 9 | body: json 10 | auth: none 11 | } 12 | 13 | body:json { 14 | { 15 | "apikey": "a85992a13ab7db424c239c50826116366e9f4fd8c591345a2d23aad01ffa4d00", 16 | "symbol": "M&M", 17 | "exchange": "NSE" 18 | 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /broker/fivepaisa/streaming/__init__.py: -------------------------------------------------------------------------------- 1 | from .fivepaisa_adapter import FivepaisaWebSocketAdapter 2 | from .fivepaisa_websocket import FivePaisaWebSocket 3 | from .fivepaisa_mapping import FivePaisaExchangeMapper, FivePaisaCapabilityRegistry 4 | 5 | __all__ = [ 6 | 'FivepaisaWebSocketAdapter', 7 | 'FivePaisaWebSocket', 8 | 'FivePaisaExchangeMapper', 9 | 'FivePaisaCapabilityRegistry' 10 | ] 11 | -------------------------------------------------------------------------------- /broker/zebu/streaming/__init__.py: -------------------------------------------------------------------------------- 1 | """ 2 | Zebu WebSocket streaming module for OpenAlgo 3 | """ 4 | from .zebu_adapter import ZebuWebSocketAdapter 5 | from .zebu_websocket import ZebuWebSocket 6 | from .zebu_mapping import ZebuExchangeMapper, ZebuCapabilityRegistry 7 | 8 | __all__ = [ 9 | 'ZebuWebSocketAdapter', 10 | 'ZebuWebSocket', 11 | 'ZebuExchangeMapper', 12 | 'ZebuCapabilityRegistry' 13 | ] -------------------------------------------------------------------------------- /collections/openalgo/CancelAllOrder.bru: -------------------------------------------------------------------------------- 1 | meta { 2 | name: CancelAllOrder 3 | type: http 4 | seq: 7 5 | } 6 | 7 | post { 8 | url: http://127.0.0.1:5000/api/v1/cancelallorder 9 | body: json 10 | auth: none 11 | } 12 | 13 | body:json { 14 | { 15 | "apikey": "a85992a13ab7db424c239c50826116366e9f4fd8c591345a2d23aad01ffa4d00", 16 | "strategy": "Test Strategy" 17 | 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /collections/openalgo/syntheticfuture.bru: -------------------------------------------------------------------------------- 1 | meta { 2 | name: Synthetic Future 3 | type: http 4 | seq: 12 5 | } 6 | 7 | post { 8 | url: http://127.0.0.1:5000/api/v1/syntheticfuture 9 | body: json 10 | auth: none 11 | } 12 | 13 | body:json { 14 | { 15 | "apikey": "", 16 | "underlying": "NIFTY", 17 | "exchange": "NSE_INDEX", 18 | "expiry_date": "30DEC25" 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /collections/openalgo/CloseAllPositions.bru: -------------------------------------------------------------------------------- 1 | meta { 2 | name: CloseAllPositions 3 | type: http 4 | seq: 4 5 | } 6 | 7 | post { 8 | url: http://127.0.0.1:5000/api/v1/closeposition 9 | body: json 10 | auth: none 11 | } 12 | 13 | body:json { 14 | { 15 | "apikey": "a85992a13ab7db424c239c50826116366e9f4fd8c591345a2d23aad01ffa4d00", 16 | "strategy": "Test Strategy" 17 | 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /collections/openalgo/telegram.bru: -------------------------------------------------------------------------------- 1 | meta { 2 | name: Telegram Notify 3 | type: http 4 | seq: 23 5 | } 6 | 7 | post { 8 | url: http://127.0.0.1:5000/api/v1/telegram/notify 9 | body: json 10 | auth: none 11 | } 12 | 13 | body:json { 14 | { 15 | "apikey": "", 16 | "username": "your_openalgo_username", 17 | "message": "Test alert from OpenAlgo API", 18 | "priority": 5 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /collections/openalgo/CancelOrder.bru: -------------------------------------------------------------------------------- 1 | meta { 2 | name: CancelOrder 3 | type: http 4 | seq: 5 5 | } 6 | 7 | post { 8 | url: http://127.0.0.1:5000/api/v1/cancelorder 9 | body: json 10 | auth: none 11 | } 12 | 13 | body:json { 14 | { 15 | "apikey": "a85992a13ab7db424c239c50826116366e9f4fd8c591345a2d23aad01ffa4d00", 16 | "strategy": "Test Strategy", 17 | "orderid": "241214000000015" 18 | 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /broker/shoonya/streaming/__init__.py: -------------------------------------------------------------------------------- 1 | """ 2 | Shoonya WebSocket streaming module 3 | """ 4 | from .shoonya_adapter import ShoonyaWebSocketAdapter 5 | from .shoonya_mapping import ShoonyaExchangeMapper, ShoonyaCapabilityRegistry 6 | from .shoonya_websocket import ShoonyaWebSocket 7 | 8 | __all__ = [ 9 | 'ShoonyaWebSocketAdapter', 10 | 'ShoonyaExchangeMapper', 11 | 'ShoonyaCapabilityRegistry', 12 | 'ShoonyaWebSocket' 13 | ] -------------------------------------------------------------------------------- /collections/openalgo/chart_update.bru: -------------------------------------------------------------------------------- 1 | meta { 2 | name: chart_update 3 | type: http 4 | seq: 42 5 | } 6 | 7 | post { 8 | url: http://127.0.0.1:5000/api/v1/chart 9 | body: json 10 | auth: none 11 | } 12 | 13 | body:json { 14 | { 15 | "apikey": "a85992a13ab7db424c239c50826116366e9f4fd8c591345a2d23aad01ffa4d00", 16 | "tv_theme": "dark", 17 | "tv_chart_layout": "{\"timeframe\":\"1D\"}" 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /collections/openalgo/optionsymbol.bru: -------------------------------------------------------------------------------- 1 | meta { 2 | name: Option Symbol 3 | type: http 4 | seq: 14 5 | } 6 | 7 | post { 8 | url: http://127.0.0.1:5000/api/v1/optionsymbol 9 | body: json 10 | auth: none 11 | } 12 | 13 | body:json { 14 | { 15 | "apikey": "", 16 | "exchange": "NSE_INDEX", 17 | "expiry_date": "30DEC25", 18 | "offset": "ATM", 19 | "option_type": "CE", 20 | "underlying": "NIFTY" 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /broker/flattrade/streaming/__init__.py: -------------------------------------------------------------------------------- 1 | """ 2 | Flattrade WebSocket streaming module 3 | """ 4 | from .flattrade_adapter import FlattradeWebSocketAdapter 5 | from .flattrade_mapping import FlattradeExchangeMapper, FlattradeCapabilityRegistry 6 | from .flattrade_websocket import FlattradeWebSocket 7 | 8 | __all__ = [ 9 | 'FlattradeWebSocketAdapter', 10 | 'FlattradeExchangeMapper', 11 | 'FlattradeCapabilityRegistry', 12 | 'FlattradeWebSocket' 13 | ] -------------------------------------------------------------------------------- /SECURITY.md: -------------------------------------------------------------------------------- 1 | # Security Policy 2 | 3 | 4 | ## Reporting a Vulnerability 5 | 6 | If you suspect you have found a security vulnerability, send an email to rajandran@marketcalls.in Please include a clear and concise description of what the vulnerability is, where it is exposed in the code, and (if known) what best practices might apply to patching it. 7 | 8 | All reports will be reviewed in a timely manner. If the issue is confirmed, a patch will be released as soon as possible. 9 | -------------------------------------------------------------------------------- /strategies/.gitignore: -------------------------------------------------------------------------------- 1 | # Strategy configuration file (contains user-specific data) 2 | strategy_configs.json 3 | 4 | # Environment variables (contains sensitive data) 5 | strategy_env.json 6 | .secure_env 7 | 8 | # Uploaded strategy scripts 9 | scripts/*.py 10 | 11 | # Keep example strategies 12 | !examples/ 13 | !examples/*.py 14 | 15 | # Ignore any backup files 16 | *.bak 17 | *.backup 18 | *~ 19 | 20 | # Python cache 21 | __pycache__/ 22 | *.pyc 23 | *.pyo 24 | *.pyd -------------------------------------------------------------------------------- /strategies/scripts/.gitignore: -------------------------------------------------------------------------------- 1 | # Ignore all strategy scripts uploaded by users 2 | *.py 3 | 4 | # But keep this .gitignore file 5 | !.gitignore 6 | 7 | # Ignore log files if any 8 | *.log 9 | 10 | # Ignore backup files 11 | *.bak 12 | *.backup 13 | *~ 14 | 15 | # Ignore Python cache 16 | __pycache__/ 17 | *.pyc 18 | *.pyo 19 | *.pyd 20 | 21 | # Keep the directory but ignore contents 22 | # This ensures the scripts folder exists in git but user strategies are not tracked -------------------------------------------------------------------------------- /examples/python/multiquotes_example.py: -------------------------------------------------------------------------------- 1 | from openalgo import api 2 | 3 | # Initialize client 4 | client = api(api_key="c32eb9dee6673190bb9dfab5f18ef0a96b0d76ba484cd36bc5ca5f7ebc8745bf", host="http://127.0.0.1:5000") 5 | 6 | # Fetch multiple quotes 7 | response = client.multiquotes(symbols=[ 8 | {"symbol": "RELIANCE", "exchange": "NSE"}, 9 | {"symbol": "TCS", "exchange": "NSE"}, 10 | {"symbol": "INFY", "exchange": "NSE"} 11 | ]) 12 | 13 | print(response) 14 | 15 | -------------------------------------------------------------------------------- /broker/mstock/streaming/__init__.py: -------------------------------------------------------------------------------- 1 | """ 2 | mstock WebSocket Streaming Module 3 | 4 | This module provides WebSocket streaming capabilities for mstock broker, 5 | following the OpenAlgo adapter pattern. 6 | """ 7 | 8 | from .mstock_adapter import MstockWebSocketAdapter 9 | from .mstock_mapping import MstockExchangeMapper, MstockCapabilityRegistry 10 | 11 | __all__ = [ 12 | 'MstockWebSocketAdapter', 13 | 'MstockExchangeMapper', 14 | 'MstockCapabilityRegistry' 15 | ] 16 | -------------------------------------------------------------------------------- /collections/openalgo/openposition.bru: -------------------------------------------------------------------------------- 1 | meta { 2 | name: openposition 3 | type: http 4 | seq: 20 5 | } 6 | 7 | post { 8 | url: http://127.0.0.1:5000/api/v1/openposition 9 | body: json 10 | auth: none 11 | } 12 | 13 | body:json { 14 | { 15 | "apikey": "a85992a13ab7db424c239c50826116366e9f4fd8c591345a2d23aad01ffa4d00", 16 | "strategy": "Test Strategy", 17 | "symbol": "YESBANK", 18 | "exchange": "NSE", 19 | "product": "CNC" 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /collections/openalgo/history (EOD).bru: -------------------------------------------------------------------------------- 1 | meta { 2 | name: history (EOD) 3 | type: http 4 | seq: 10 5 | } 6 | 7 | post { 8 | url: http://127.0.0.1:5000/api/v1/history 9 | body: json 10 | auth: none 11 | } 12 | 13 | body:json { 14 | { 15 | "apikey": "a85992a13ab7db424c239c50826116366e9f4fd8c591345a2d23aad01ffa4d00", 16 | "symbol": "SBIN", 17 | "exchange": "NSE", 18 | "interval": "D", 19 | "start_date": "2025-01-01", 20 | "end_date": "2025-11-20" 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /broker/indmoney/streaming/__init__.py: -------------------------------------------------------------------------------- 1 | # INDmoney WebSocket Streaming Module 2 | 3 | from .indWebSocket import IndWebSocket 4 | from .indmoney_adapter import IndmoneyWebSocketAdapter 5 | from .indmoney_mapping import ( 6 | IndmoneyExchangeMapper, 7 | IndmoneyModeMapper, 8 | IndmoneyCapabilityRegistry 9 | ) 10 | 11 | __all__ = [ 12 | 'IndWebSocket', 13 | 'IndmoneyWebSocketAdapter', 14 | 'IndmoneyExchangeMapper', 15 | 'IndmoneyModeMapper', 16 | 'IndmoneyCapabilityRegistry' 17 | ] 18 | -------------------------------------------------------------------------------- /collections/openalgo/history (intraday).bru: -------------------------------------------------------------------------------- 1 | meta { 2 | name: history (intraday) 3 | type: http 4 | seq: 9 5 | } 6 | 7 | post { 8 | url: http://127.0.0.1:5000/api/v1/history 9 | body: json 10 | auth: none 11 | } 12 | 13 | body:json { 14 | { 15 | "apikey": "a85992a13ab7db424c239c50826116366e9f4fd8c591345a2d23aad01ffa4d00", 16 | "symbol": "SBIN", 17 | "exchange": "NSE", 18 | "interval": "1m", 19 | "start_date": "2025-10-15", 20 | "end_date": "2025-10-20" 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /templates/components/pagination.html: -------------------------------------------------------------------------------- 1 | {% if total_pages > 1 %} 2 |
3 |
4 | {% for page_num in range(1, total_pages + 1) %} 5 | 10 | {% endfor %} 11 |
12 |
13 | {% endif %} 14 | -------------------------------------------------------------------------------- /broker/motilal/streaming/__init__.py: -------------------------------------------------------------------------------- 1 | """ 2 | Motilal Oswal WebSocket streaming module for OpenAlgo. 3 | 4 | This module provides WebSocket streaming functionality for Motilal Oswal broker, 5 | integrating with OpenAlgo's WebSocket proxy infrastructure. 6 | """ 7 | 8 | from .motilal_adapter import MotilalWebSocketAdapter 9 | from .motilal_mapping import MotilalExchangeMapper, MotilalCapabilityRegistry 10 | 11 | __all__ = [ 12 | 'MotilalWebSocketAdapter', 13 | 'MotilalExchangeMapper', 14 | 'MotilalCapabilityRegistry' 15 | ] 16 | -------------------------------------------------------------------------------- /blueprints/platforms.py: -------------------------------------------------------------------------------- 1 | # blueprints/platforms.py 2 | 3 | from flask import Blueprint, render_template 4 | from utils.session import check_session_validity 5 | import logging 6 | 7 | logger = logging.getLogger(__name__) 8 | 9 | platforms_bp = Blueprint('platforms_bp', __name__, url_prefix='/platforms') 10 | 11 | @platforms_bp.route('/', methods=['GET']) 12 | @check_session_validity 13 | def index(): 14 | """Display all trading platforms""" 15 | logger.info("Accessing platforms page") 16 | return render_template('platforms.html') 17 | -------------------------------------------------------------------------------- /static/js/mobile-menu.js: -------------------------------------------------------------------------------- 1 | // Functions for mobile menu toggle (existing functionality) 2 | function toggleMobileMenu() { 3 | var menu = document.getElementById('mobile-menu'); 4 | menu.classList.remove('-translate-x-full'); 5 | document.querySelector('button[onclick="toggleMobileMenu()"]').style.display = 'none'; 6 | } 7 | 8 | function closeMobileMenu() { 9 | var menu = document.getElementById('mobile-menu'); 10 | menu.classList.add('-translate-x-full'); 11 | document.querySelector('button[onclick="toggleMobileMenu()"]').style.display = 'block'; 12 | } 13 | -------------------------------------------------------------------------------- /broker/dhan/api/baseurl.py: -------------------------------------------------------------------------------- 1 | # Dhan API Base URL Configuration 2 | 3 | # Base URL for Dhan API endpoints 4 | BASE_URL = "https://api.dhan.co" 5 | 6 | # Function to build full URL with endpoint 7 | def get_url(endpoint): 8 | """ 9 | Constructs a full URL by combining the base URL and the endpoint 10 | 11 | Args: 12 | endpoint (str): The API endpoint path (should start with '/') 13 | 14 | Returns: 15 | str: The complete URL 16 | """ 17 | if not endpoint.startswith('/'): 18 | endpoint = '/' + endpoint 19 | return BASE_URL + endpoint 20 | -------------------------------------------------------------------------------- /collections/openalgo/SplitOrder.bru: -------------------------------------------------------------------------------- 1 | meta { 2 | name: SplitOrder 3 | type: http 4 | seq: 18 5 | } 6 | 7 | post { 8 | url: http://127.0.0.1:5000/api/v1/splitorder 9 | body: json 10 | auth: none 11 | } 12 | 13 | body:json { 14 | { 15 | "apikey": "38f99d7d226cc0c3baa19dcacf0b1f049d2f68371da1dda2c97b1b63a3a9ca2e", 16 | "strategy": "Test Strategy", 17 | "exchange": "NSE", 18 | "symbol": "YESBANK", 19 | "action": "SELL", 20 | "quantity": "105", 21 | "splitsize": "20", 22 | "pricetype": "MARKET", 23 | "product": "MIS" 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /broker/dhan_sandbox/api/baseurl.py: -------------------------------------------------------------------------------- 1 | # Dhan API Base URL Configuration 2 | 3 | # Base URL for Dhan API endpoints 4 | BASE_URL = "https://sandbox.dhan.co" 5 | 6 | # Function to build full URL with endpoint 7 | def get_url(endpoint): 8 | """ 9 | Constructs a full URL by combining the base URL and the endpoint 10 | 11 | Args: 12 | endpoint (str): The API endpoint path (should start with '/') 13 | 14 | Returns: 15 | str: The complete URL 16 | """ 17 | if not endpoint.startswith('/'): 18 | endpoint = '/' + endpoint 19 | return BASE_URL + endpoint 20 | -------------------------------------------------------------------------------- /broker/indmoney/api/baseurl.py: -------------------------------------------------------------------------------- 1 | # IndMoney API Base URL Configuration 2 | 3 | # Base URL for Indmoney API endpoints 4 | BASE_URL = "https://api.indstocks.com" 5 | 6 | # Function to build full URL with endpoint 7 | def get_url(endpoint): 8 | """ 9 | Constructs a full URL by combining the base URL and the endpoint 10 | 11 | Args: 12 | endpoint (str): The API endpoint path (should start with '/') 13 | 14 | Returns: 15 | str: The complete URL 16 | """ 17 | if not endpoint.startswith('/'): 18 | endpoint = '/' + endpoint 19 | return BASE_URL + endpoint 20 | -------------------------------------------------------------------------------- /extensions.py: -------------------------------------------------------------------------------- 1 | from flask_socketio import SocketIO 2 | 3 | # Disable eventlet to prevent greenlet threading errors 4 | # This fixes concurrent order placement issues in Docker 5 | # Added error handling for disconnected sessions 6 | socketio = SocketIO( 7 | cors_allowed_origins='*', 8 | async_mode='threading', 9 | ping_timeout=10, # Time in seconds before considering the connection lost 10 | ping_interval=5, # Interval in seconds between pings 11 | logger=False, # Disable built-in logging to avoid noise from disconnection errors 12 | engineio_logger=False # Disable engine.io logging 13 | ) 14 | -------------------------------------------------------------------------------- /collections/openalgo/margin.bru: -------------------------------------------------------------------------------- 1 | meta { 2 | name: Margin Calculator 3 | type: http 4 | seq: 21 5 | } 6 | 7 | post { 8 | url: http://127.0.0.1:5000/api/v1/margin 9 | body: json 10 | auth: none 11 | } 12 | 13 | body:json { 14 | { 15 | "apikey": "", 16 | "positions": [ 17 | { 18 | "symbol": "NIFTY30DEC2526000CE", 19 | "exchange": "NFO", 20 | "action": "SELL", 21 | "product": "NRML", 22 | "pricetype": "MARKET", 23 | "quantity": "75", 24 | "price": "0" 25 | } 26 | ] 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /collections/openalgo/optionsorder.bru: -------------------------------------------------------------------------------- 1 | meta { 2 | name: Options Order 3 | type: http 4 | seq: 5 5 | } 6 | 7 | post { 8 | url: http://127.0.0.1:5000/api/v1/optionsorder 9 | body: json 10 | auth: none 11 | } 12 | 13 | body:json { 14 | { 15 | "apikey": "", 16 | "strategy": "test_strategy", 17 | "underlying": "NIFTY", 18 | "exchange": "NSE_INDEX", 19 | "expiry_date": "30DEC25", 20 | "offset": "ATM", 21 | "option_type": "CE", 22 | "action": "BUY", 23 | "quantity": 75, 24 | "pricetype": "MARKET", 25 | "product": "MIS", 26 | "splitsize": 0 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /utils.py: -------------------------------------------------------------------------------- 1 | from datetime import datetime, timedelta 2 | import pytz 3 | from utils.logging import get_logger 4 | 5 | # Initialize logger 6 | logger = get_logger(__name__) 7 | 8 | 9 | def get_session_expiry_time(): 10 | now_utc = datetime.now(pytz.timezone('UTC')) 11 | now_ist = now_utc.astimezone(pytz.timezone('Asia/Kolkata')) 12 | logger.debug(f"Current IST time: {now_ist}") 13 | target_time_ist = now_ist.replace(hour=3, minute=00, second=0, microsecond=0) 14 | if now_ist > target_time_ist: 15 | target_time_ist += timedelta(days=1) 16 | remaining_time = target_time_ist - now_ist 17 | return remaining_time -------------------------------------------------------------------------------- /collections/openalgo/PlaceOrder.bru: -------------------------------------------------------------------------------- 1 | meta { 2 | name: PlaceOrder 3 | type: http 4 | seq: 3 5 | } 6 | 7 | post { 8 | url: http://127.0.0.1:5000/api/v1/placeorder 9 | body: json 10 | auth: none 11 | } 12 | 13 | body:json { 14 | { 15 | "apikey": "a85992a13ab7db424c239c50826116366e9f4fd8c591345a2d23aad01ffa4d00", 16 | "strategy": "My Strategy", 17 | "exchange": "NSE", 18 | "symbol": "NHPC", 19 | "action": "BUY", 20 | "product": "MIS", 21 | "pricetype": "MARKET", 22 | "quantity": "1", 23 | "price": "0", 24 | "trigger_price": "0", 25 | "disclosed_quantity": "0" 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /docs/prompt/order-constants.md: -------------------------------------------------------------------------------- 1 | # Order Constants 2 | 3 | ## Order Constants 4 | 5 | ### Exchange 6 | 7 | * NSE: NSE Equity 8 | * NFO: NSE Futures & Options 9 | * CDS: NSE Currency 10 | * BSE: BSE Equity 11 | * BFO: BSE Futures & Options 12 | * BCD: BSE Currency 13 | * MCX: MCX Commodity 14 | * NCDEX: NCDEX Commodity 15 | 16 | ### Product Type 17 | 18 | * CNC: Cash & Carry for equity 19 | * NRML: Normal for futures and options 20 | * MIS: Intraday Square off 21 | 22 | ### Price Type 23 | 24 | * MARKET: Market Order 25 | * LIMIT: Limit Order 26 | * SL: Stop Loss Limit Order 27 | * SL-M: Stop Loss Market Order 28 | 29 | ### Action 30 | 31 | * BUY: Buy 32 | * SELL: Sell -------------------------------------------------------------------------------- /static/favicon/safari-pinned-tab.svg: -------------------------------------------------------------------------------- 1 | 2 | 4 | 7 | 8 | Created by potrace 1.14, written by Peter Selinger 2001-2017 9 | 10 | 12 | 13 | 14 | 15 | -------------------------------------------------------------------------------- /broker/iifl/api/margin_api.py: -------------------------------------------------------------------------------- 1 | from utils.logging import get_logger 2 | 3 | logger = get_logger(__name__) 4 | 5 | def calculate_margin_api(positions, auth): 6 | """ 7 | Calculate margin requirement for a basket of positions. 8 | 9 | Note: IIFL does not provide a margin calculator API. 10 | 11 | Args: 12 | positions: List of positions in OpenAlgo format 13 | auth: Authentication token for IIFL 14 | 15 | Raises: 16 | NotImplementedError: IIFL does not support margin calculator API 17 | """ 18 | logger.warning("IIFL does not provide margin calculator API") 19 | raise NotImplementedError("IIFL does not support margin calculator API") 20 | -------------------------------------------------------------------------------- /utils/config.py: -------------------------------------------------------------------------------- 1 | # utils/config.py 2 | 3 | from dotenv import load_dotenv 4 | import os 5 | 6 | # Load environment variables from .env file with override=True to ensure values are updated 7 | load_dotenv(override=True) 8 | 9 | def get_broker_api_key(): 10 | return os.getenv('BROKER_API_KEY') 11 | 12 | def get_broker_api_secret(): 13 | return os.getenv('BROKER_API_SECRET') 14 | 15 | def get_login_rate_limit_min(): 16 | return os.getenv("LOGIN_RATE_LIMIT_MIN", "5 per minute") 17 | 18 | def get_login_rate_limit_hour(): 19 | return os.getenv("LOGIN_RATE_LIMIT_HOUR", "25 per hour") 20 | 21 | def get_host_server(): 22 | return os.getenv('HOST_SERVER', 'http://127.0.0.1:5000') 23 | -------------------------------------------------------------------------------- /broker/ibulls/api/margin_api.py: -------------------------------------------------------------------------------- 1 | from utils.logging import get_logger 2 | 3 | logger = get_logger(__name__) 4 | 5 | def calculate_margin_api(positions, auth): 6 | """ 7 | Calculate margin requirement for a basket of positions. 8 | 9 | Note: IBulls does not provide a margin calculator API. 10 | 11 | Args: 12 | positions: List of positions in OpenAlgo format 13 | auth: Authentication token for IBulls 14 | 15 | Raises: 16 | NotImplementedError: IBulls does not support margin calculator API 17 | """ 18 | logger.warning("IBulls does not provide margin calculator API") 19 | raise NotImplementedError("IBulls does not support margin calculator API") 20 | -------------------------------------------------------------------------------- /broker/wisdom/api/margin_api.py: -------------------------------------------------------------------------------- 1 | from utils.logging import get_logger 2 | 3 | logger = get_logger(__name__) 4 | 5 | def calculate_margin_api(positions, auth): 6 | """ 7 | Calculate margin requirement for a basket of positions. 8 | 9 | Note: Wisdom does not provide a margin calculator API. 10 | 11 | Args: 12 | positions: List of positions in OpenAlgo format 13 | auth: Authentication token for Wisdom 14 | 15 | Raises: 16 | NotImplementedError: Wisdom does not support margin calculator API 17 | """ 18 | logger.warning("Wisdom does not provide margin calculator API") 19 | raise NotImplementedError("Wisdom does not support margin calculator API") 20 | -------------------------------------------------------------------------------- /collections/openalgo/Chartink.bru: -------------------------------------------------------------------------------- 1 | meta { 2 | name: Chartink 3 | type: http 4 | seq: 21 5 | } 6 | 7 | post { 8 | url: http://8d32-49-207-193-1.ngrok-free.app/chartink/webhook/0fd7ee15-c83b-4f6e-97ae-68cb4b9bdf95 9 | body: json 10 | auth: none 11 | } 12 | 13 | body:json { 14 | { 15 | "stocks": "RELIANCE", 16 | "trigger_prices": "3.75,541.8,2.1,0.2,329.6,166.8,1.25", 17 | "triggered_at": "2:34 pm", 18 | "scan_name": "SELL", 19 | "scan_url": "short-term-breakouts", 20 | "alert_name": "Alert for Short term breakouts", 21 | "webhook_url": "http://8d32-49-207-193-1.ngrok-free.app/chartink/webhook/835e678a-b278-4371-bb4a-f8055426b28e" 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /collections/openalgo/PlaceSmartOrder.bru: -------------------------------------------------------------------------------- 1 | meta { 2 | name: PlaceSmartOrder 3 | type: http 4 | seq: 2 5 | } 6 | 7 | post { 8 | url: http://127.0.0.1:5000/api/v1/placesmartorder 9 | body: json 10 | auth: none 11 | } 12 | 13 | body:json { 14 | { 15 | "apikey": "a85992a13ab7db424c239c50826116366e9f4fd8c591345a2d23aad01ffa4d00", 16 | "strategy": "Test Strategy", 17 | "exchange": "NSE", 18 | "symbol": "YESBANK", 19 | "action": "BUY", 20 | "product": "MIS", 21 | "pricetype": "MARKET", 22 | "quantity": "10", 23 | "price": "0", 24 | "trigger_price": "0", 25 | "disclosed_quantity": "0", 26 | "position_size": "10" 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /collections/postman/openalgo.postman_environment.json: -------------------------------------------------------------------------------- 1 | { 2 | "id": "7f3a1196-6606-4bf1-a4ce-45e756b062a9", 3 | "name": "openalgo", 4 | "values": [ 5 | { 6 | "key": "base", 7 | "value": "http://127.0.0.1:5000/", 8 | "type": "default", 9 | "enabled": true 10 | }, 11 | { 12 | "key": "base-ws", 13 | "value": "ws://127.0.0.1:8765", 14 | "type": "default", 15 | "enabled": true 16 | }, 17 | { 18 | "key": "apiKey", 19 | "value": "your-openAlgo-Api-key", 20 | "type": "default", 21 | "enabled": true 22 | } 23 | ], 24 | "_postman_variable_scope": "environment", 25 | "_postman_exported_at": "2025-07-25T18:24:02.290Z", 26 | "_postman_exported_using": "Postman/11.55.0" 27 | } -------------------------------------------------------------------------------- /collections/openalgo/ModifyOrder.bru: -------------------------------------------------------------------------------- 1 | meta { 2 | name: ModifyOrder 3 | type: http 4 | seq: 6 5 | } 6 | 7 | post { 8 | url: http://127.0.0.1:5000/api/v1/modifyorder 9 | body: json 10 | auth: none 11 | } 12 | 13 | body:json { 14 | { 15 | "apikey": "a85992a13ab7db424c239c50826116366e9f4fd8c591345a2d23aad01ffa4d00", 16 | "strategy": "Test Message", 17 | "symbol": "GOLDPETAL31OCT24FUT", 18 | "action": "BUY", 19 | "exchange": "MCX", 20 | "orderid":"427510324509895", 21 | "product":"MIS", 22 | "pricetype":"LIMIT", 23 | "price":"7410", 24 | "quantity":"1", 25 | "disclosed_quantity":"0", 26 | "trigger_price":"0" 27 | } 28 | 29 | } 30 | -------------------------------------------------------------------------------- /.dockerignore: -------------------------------------------------------------------------------- 1 | # Ignore stateful content (these will be generated again) 2 | db/ 3 | log/ 4 | 5 | # Byte-compiled / cache files 6 | __pycache__/ 7 | *.py[cod] 8 | *.pyo 9 | *.pyd 10 | 11 | # Virtual environments 12 | env/ 13 | venv/ 14 | .venv/ 15 | .Python 16 | 17 | # Environment and secrets 18 | .env 19 | !.sample.env 20 | secrets.* 21 | *.key 22 | *.pem 23 | 24 | # SQLite and runtime files 25 | *.sqlite 26 | *.sqlite3 27 | *.db 28 | *.log 29 | *.sock 30 | 31 | # Version control 32 | .git/ 33 | .gitignore 34 | 35 | # Docker and CI/CD 36 | Dockerfile 37 | docker-compose.yml 38 | .docker/ 39 | .dockerignore 40 | 41 | # IDE config 42 | .vscode/ 43 | .idea/ 44 | 45 | # Project docs (EXPLICITLY INCLUDED) 46 | !docs/ 47 | -------------------------------------------------------------------------------- /broker/aliceblue/api/margin_api.py: -------------------------------------------------------------------------------- 1 | from utils.logging import get_logger 2 | 3 | logger = get_logger(__name__) 4 | 5 | def calculate_margin_api(positions, auth): 6 | """ 7 | Calculate margin requirement for a basket of positions. 8 | 9 | Note: AliceBlue does not provide a margin calculator API. 10 | 11 | Args: 12 | positions: List of positions in OpenAlgo format 13 | auth: Authentication token for AliceBlue 14 | 15 | Raises: 16 | NotImplementedError: AliceBlue does not support margin calculator API 17 | """ 18 | logger.warning("AliceBlue does not provide margin calculator API") 19 | raise NotImplementedError("AliceBlue does not support margin calculator API") 20 | -------------------------------------------------------------------------------- /broker/jainamxts/api/margin_api.py: -------------------------------------------------------------------------------- 1 | from utils.logging import get_logger 2 | 3 | logger = get_logger(__name__) 4 | 5 | def calculate_margin_api(positions, auth): 6 | """ 7 | Calculate margin requirement for a basket of positions. 8 | 9 | Note: JainamXTS does not provide a margin calculator API. 10 | 11 | Args: 12 | positions: List of positions in OpenAlgo format 13 | auth: Authentication token for JainamXTS 14 | 15 | Raises: 16 | NotImplementedError: JainamXTS does not support margin calculator API 17 | """ 18 | logger.warning("JainamXTS does not provide margin calculator API") 19 | raise NotImplementedError("JainamXTS does not support margin calculator API") 20 | -------------------------------------------------------------------------------- /broker/pocketful/api/margin_api.py: -------------------------------------------------------------------------------- 1 | from utils.logging import get_logger 2 | 3 | logger = get_logger(__name__) 4 | 5 | def calculate_margin_api(positions, auth): 6 | """ 7 | Calculate margin requirement for a basket of positions. 8 | 9 | Note: Pocketful does not provide a margin calculator API. 10 | 11 | Args: 12 | positions: List of positions in OpenAlgo format 13 | auth: Authentication token for Pocketful 14 | 15 | Raises: 16 | NotImplementedError: Pocketful does not support margin calculator API 17 | """ 18 | logger.warning("Pocketful does not provide margin calculator API") 19 | raise NotImplementedError("Pocketful does not support margin calculator API") 20 | -------------------------------------------------------------------------------- /broker/compositedge/api/margin_api.py: -------------------------------------------------------------------------------- 1 | from utils.logging import get_logger 2 | 3 | logger = get_logger(__name__) 4 | 5 | def calculate_margin_api(positions, auth): 6 | """ 7 | Calculate margin requirement for a basket of positions. 8 | 9 | Note: Compositedge does not provide a margin calculator API. 10 | 11 | Args: 12 | positions: List of positions in OpenAlgo format 13 | auth: Authentication token for Compositedge 14 | 15 | Raises: 16 | NotImplementedError: Compositedge does not support margin calculator API 17 | """ 18 | logger.warning("Compositedge does not provide margin calculator API") 19 | raise NotImplementedError("Compositedge does not support margin calculator API") 20 | -------------------------------------------------------------------------------- /broker/fivepaisaxts/api/margin_api.py: -------------------------------------------------------------------------------- 1 | from utils.logging import get_logger 2 | 3 | logger = get_logger(__name__) 4 | 5 | def calculate_margin_api(positions, auth): 6 | """ 7 | Calculate margin requirement for a basket of positions. 8 | 9 | Note: FivePaisaXTS does not provide a margin calculator API. 10 | 11 | Args: 12 | positions: List of positions in OpenAlgo format 13 | auth: Authentication token for FivePaisaXTS 14 | 15 | Raises: 16 | NotImplementedError: FivePaisaXTS does not support margin calculator API 17 | """ 18 | logger.warning("FivePaisaXTS does not provide margin calculator API") 19 | raise NotImplementedError("FivePaisaXTS does not support margin calculator API") 20 | -------------------------------------------------------------------------------- /broker/motilal/api/margin_api.py: -------------------------------------------------------------------------------- 1 | from utils.logging import get_logger 2 | 3 | logger = get_logger(__name__) 4 | 5 | def calculate_margin_api(positions, auth): 6 | """ 7 | Calculate margin requirement for a basket of positions. 8 | 9 | Note: Motilal Oswal does not provide a margin calculator API. 10 | 11 | Args: 12 | positions: List of positions in OpenAlgo format 13 | auth: Authentication token for Motilal Oswal 14 | 15 | Raises: 16 | NotImplementedError: Motilal Oswal does not support margin calculator API 17 | """ 18 | logger.warning("Motilal Oswal does not provide margin calculator API") 19 | raise NotImplementedError("Motilal Oswal does not support margin calculator API") 20 | -------------------------------------------------------------------------------- /collections/openalgo/multiquotes.bru: -------------------------------------------------------------------------------- 1 | meta { 2 | name: multiquotes 3 | type: http 4 | seq: 9 5 | } 6 | 7 | post { 8 | url: http://127.0.0.1:5000/api/v1/multiquotes 9 | body: json 10 | auth: none 11 | } 12 | 13 | body:json { 14 | { 15 | "apikey": "a85992a13ab7db424c239c50826116366e9f4fd8c591345a2d23aad01ffa4d00", 16 | "symbols": [ 17 | { 18 | "symbol": "SBIN", 19 | "exchange": "NSE" 20 | }, 21 | { 22 | "symbol": "TCS", 23 | "exchange": "NSE" 24 | }, 25 | { 26 | "symbol": "INFY", 27 | "exchange": "BSE" 28 | }, 29 | { 30 | "symbol": "NIFTY30DEC25FUT", 31 | "exchange": "NFO" 32 | } 33 | ] 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /templates/logs.html: -------------------------------------------------------------------------------- 1 | {% extends 'base.html' %} 2 | 3 | {% block head %} 4 | {% include 'components/logs_styles.html' %} 5 | {% endblock %} 6 | 7 | {% block content %} 8 |
9 | 10 | {% include 'components/logs_filters.html' %} 11 | 12 | 13 | {% include 'components/loading_spinner.html' %} 14 | 15 | 16 |
17 | {% for log in logs %} 18 | {% include 'components/log_entry.html' %} 19 | {% endfor %} 20 |
21 | 22 | 23 | {% include 'components/pagination.html' %} 24 |
25 | 26 | {% include 'components/logs_scripts.html' %} 27 | {% endblock %} 28 | -------------------------------------------------------------------------------- /sandbox/__init__.py: -------------------------------------------------------------------------------- 1 | # sandbox/__init__.py 2 | """ 3 | Sandbox Mode - API Analyzer Environment 4 | 5 | OpenAlgo is an open-source application that provides Sandbox Mode (API Analyzer) 6 | to make it easier for traders to test strategies in a realistic simulated 7 | environment without executing real trades through a broker. 8 | 9 | Key Features: 10 | - ₹10,000,000 (1 Crore) starting sandbox capital (configurable) 11 | - Auto reset every Sunday at midnight IST (configurable) 12 | - Real market data integration 13 | - Realistic order execution simulation 14 | - Position and holdings management 15 | - Leverage-based margin calculations 16 | - Auto square-off for MIS positions 17 | - T+1 settlement for CNC holdings 18 | - Self-hosted, transparent, open-source testing environment 19 | """ 20 | 21 | __version__ = '1.0.0' 22 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | venopenalgo 2 | .venv/ 3 | .env 4 | openalgo.db 5 | *.db-shm 6 | *.db-wal 7 | *.db-journal 8 | .env 9 | .flaskenv 10 | *.pyc 11 | __pycache__/ 12 | instance/ 13 | .DS_Store 14 | Thumbs.db 15 | .vscode/ 16 | .idea/ 17 | .python-version 18 | config.ini 19 | .venvv 20 | 21 | #DB Files, CSV Files and Log/Tmp Files 22 | 23 | *.egg-info/ 24 | *.db 25 | *.sqlite 26 | *.log 27 | *.tmp 28 | checkpoint.txt 29 | checkpoints.json 30 | 31 | # Strategy encryption key 32 | db/strategy_encryption.key 33 | 34 | # CSV Files (except symbols.csv) 35 | *.csv 36 | !symbols.csv 37 | !all_symbols.csv 38 | 39 | # Environments 40 | .env 41 | .venv 42 | env/ 43 | venv/ 44 | ENV/ 45 | env.bak/ 46 | venv.bak/ 47 | 48 | # Node modules 49 | /node_modules/ 50 | 51 | # Build output 52 | /dist/ 53 | /build/ 54 | /.next/ 55 | /out/ 56 | 57 | # Tailwind CSS output 58 | # **/output.css 59 | -------------------------------------------------------------------------------- /broker/indmoney/api/auth_api.py: -------------------------------------------------------------------------------- 1 | import httpx 2 | import json 3 | import os 4 | from utils.httpx_client import get_httpx_client 5 | from broker.indmoney.api.baseurl import get_url, BASE_URL 6 | 7 | 8 | 9 | def authenticate_broker(code): 10 | try: 11 | BROKER_API_KEY = os.getenv('BROKER_API_KEY') 12 | BROKER_API_SECRET = os.getenv('BROKER_API_SECRET') 13 | REDIRECT_URL = os.getenv('REDIRECT_URL') 14 | 15 | # For IndMoney, the access token is directly provided in BROKER_API_SECRET 16 | # No OAuth flow needed - just return the access token 17 | if BROKER_API_SECRET: 18 | return BROKER_API_SECRET, None 19 | else: 20 | return None, "No access token found in BROKER_API_SECRET environment variable" 21 | 22 | except Exception as e: 23 | return None, f"An exception occurred: {str(e)}" 24 | 25 | -------------------------------------------------------------------------------- /test/test_broadcast_save.py: -------------------------------------------------------------------------------- 1 | import sys 2 | import os 3 | sys.path.insert(0, os.path.dirname(os.path.dirname(os.path.abspath(__file__)))) 4 | 5 | from database.telegram_db import update_bot_config, get_bot_config 6 | 7 | # Test saving with broadcast disabled 8 | print("Testing broadcast_enabled field:") 9 | print("1. Setting broadcast_enabled to False") 10 | update_bot_config({'broadcast_enabled': False}) 11 | 12 | config = get_bot_config() 13 | print(f"2. After save - broadcast_enabled: {config.get('broadcast_enabled')}") 14 | print(f" Type: {type(config.get('broadcast_enabled'))}") 15 | 16 | # Try again with True 17 | print("\n3. Setting broadcast_enabled to True") 18 | update_bot_config({'broadcast_enabled': True}) 19 | 20 | config = get_bot_config() 21 | print(f"4. After save - broadcast_enabled: {config.get('broadcast_enabled')}") 22 | print(f" Type: {type(config.get('broadcast_enabled'))}") -------------------------------------------------------------------------------- /broker/zebu/api/margin_api.py: -------------------------------------------------------------------------------- 1 | from utils.logging import get_logger 2 | 3 | logger = get_logger(__name__) 4 | 5 | def calculate_margin_api(positions, auth): 6 | """ 7 | Calculate margin requirement for a basket of positions. 8 | 9 | Note: Zebu does not provide a position-specific margin calculator API. 10 | The available Margin API only returns account-level margin information, 11 | which is not suitable for calculating margin requirements for specific positions. 12 | 13 | Args: 14 | positions: List of positions in OpenAlgo format 15 | auth: Authentication token for Zebu 16 | 17 | Raises: 18 | NotImplementedError: Zebu does not support position-specific margin calculator API 19 | """ 20 | logger.warning("Zebu does not provide position-specific margin calculator API") 21 | raise NotImplementedError("Zebu does not support position-specific margin calculator API") 22 | -------------------------------------------------------------------------------- /collections/openalgo/BasketOrder.bru: -------------------------------------------------------------------------------- 1 | meta { 2 | name: BasketOrder 3 | type: http 4 | seq: 17 5 | } 6 | 7 | post { 8 | url: http://127.0.0.1:5000/api/v1/basketorder 9 | body: json 10 | auth: none 11 | } 12 | 13 | body:json { 14 | { 15 | "apikey": "a85992a13ab7db424c239c50826116366e9f4fd8c591345a2d23aad01ffa4d00", 16 | "strategy": "your-strategy", 17 | "orders": [ 18 | { 19 | "symbol": "RELIANCE", 20 | "exchange": "NSE", 21 | "action": "BUY", 22 | "quantity": "1", 23 | "pricetype": "MARKET", 24 | "product": "MIS" 25 | }, 26 | { 27 | "symbol": "INFY", 28 | "exchange": "NSE", 29 | "action": "SELL", 30 | "quantity": "1", 31 | "pricetype": "MARKET", 32 | "product": "MIS" 33 | } 34 | ] 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /broker/fivepaisa/api/margin_api.py: -------------------------------------------------------------------------------- 1 | from utils.logging import get_logger 2 | 3 | logger = get_logger(__name__) 4 | 5 | def calculate_margin_api(positions, auth): 6 | """ 7 | Calculate margin requirement for a basket of positions. 8 | 9 | Note: 5paisa does not provide a position-specific margin calculator API. 10 | The available Margin API only returns account-level margin information, 11 | which is not suitable for calculating margin requirements for specific positions. 12 | 13 | Args: 14 | positions: List of positions in OpenAlgo format 15 | auth: Authentication token for 5paisa 16 | 17 | Raises: 18 | NotImplementedError: 5paisa does not support position-specific margin calculator API 19 | """ 20 | logger.warning("5paisa does not provide position-specific margin calculator API") 21 | raise NotImplementedError("5paisa does not support position-specific margin calculator API") 22 | -------------------------------------------------------------------------------- /broker/tradejini/api/margin_api.py: -------------------------------------------------------------------------------- 1 | from utils.logging import get_logger 2 | 3 | logger = get_logger(__name__) 4 | 5 | def calculate_margin_api(positions, auth): 6 | """ 7 | Calculate margin requirement for a basket of positions. 8 | 9 | Note: Tradejini does not provide a position-specific margin calculator API. 10 | The available Margin API only returns account-level margin information, 11 | which is not suitable for calculating margin requirements for specific positions. 12 | 13 | Args: 14 | positions: List of positions in OpenAlgo format 15 | auth: Authentication token for Tradejini 16 | 17 | Raises: 18 | NotImplementedError: Tradejini does not support position-specific margin calculator API 19 | """ 20 | logger.warning("Tradejini does not provide position-specific margin calculator API") 21 | raise NotImplementedError("Tradejini does not support position-specific margin calculator API") 22 | -------------------------------------------------------------------------------- /broker/paytm/api/margin_api.py: -------------------------------------------------------------------------------- 1 | from utils.logging import get_logger 2 | 3 | logger = get_logger(__name__) 4 | 5 | def calculate_margin_api(positions, auth): 6 | """ 7 | Calculate margin requirement for a basket of positions. 8 | 9 | Note: Paytm Money does not provide a position-specific margin calculator API. 10 | The available Margin API only returns account-level margin information, 11 | which is not suitable for calculating margin requirements for specific positions. 12 | 13 | Args: 14 | positions: List of positions in OpenAlgo format 15 | auth: Authentication token for Paytm Money 16 | 17 | Raises: 18 | NotImplementedError: Paytm Money does not support position-specific margin calculator API 19 | """ 20 | logger.warning("Paytm Money does not provide position-specific margin calculator API") 21 | raise NotImplementedError("Paytm Money does not support position-specific margin calculator API") 22 | -------------------------------------------------------------------------------- /broker/firstock/api/margin_api.py: -------------------------------------------------------------------------------- 1 | from utils.logging import get_logger 2 | 3 | logger = get_logger(__name__) 4 | 5 | def calculate_margin_api(positions, auth): 6 | """ 7 | Calculate margin requirement for a basket of positions. 8 | 9 | Note: Firstock does not provide a position-specific margin calculator API. 10 | The available Limit API (/V1/limit) only returns account-level margin information 11 | (cash, collateral, span, expo, marginused), which is not suitable for calculating 12 | margin requirements for specific positions. 13 | 14 | Args: 15 | positions: List of positions in OpenAlgo format 16 | auth: Authentication token for Firstock 17 | 18 | Raises: 19 | NotImplementedError: Firstock does not support position-specific margin calculator API 20 | """ 21 | logger.warning("Firstock does not provide position-specific margin calculator API") 22 | raise NotImplementedError("Firstock does not support position-specific margin calculator API") 23 | -------------------------------------------------------------------------------- /playground/style.css: -------------------------------------------------------------------------------- 1 | /* Ensure full height layout */ 2 | html, body { 3 | height: 100%; 4 | overflow: hidden; 5 | } 6 | 7 | /* Custom scrollbar for webkit browsers */ 8 | ::-webkit-scrollbar { 9 | width: 8px; 10 | } 11 | ::-webkit-scrollbar-track { 12 | background: #1d232a; /* bg-base-200 */ 13 | } 14 | ::-webkit-scrollbar-thumb { 15 | background: #3d4451; /* bg-base-300 */ 16 | border-radius: 4px; 17 | } 18 | ::-webkit-scrollbar-thumb:hover { 19 | background: #6b7280; 20 | } 21 | 22 | /* Ensure the depth panel always opens centered */ 23 | #depth-panel-modal.modal-open { 24 | display: flex; 25 | justify-content: center; 26 | align-items: center; 27 | } 28 | 29 | /* Custom styles for the market panel to match the image */ 30 | .market-panel-label { 31 | @apply text-xs text-gray-400 uppercase; 32 | } 33 | .market-panel-value { 34 | @apply font-semibold font-mono; 35 | } 36 | .bid-price { 37 | @apply text-green-400; 38 | } 39 | .ask-price { 40 | @apply text-red-400; 41 | } -------------------------------------------------------------------------------- /broker/aliceblue/streaming/__init__.py: -------------------------------------------------------------------------------- 1 | """ 2 | AliceBlue WebSocket streaming module for OpenAlgo 3 | 4 | This module provides WebSocket streaming capabilities for AliceBlue broker integration. 5 | It includes: 6 | - AliceBlue WebSocket client wrapper 7 | - Message mapping and parsing utilities 8 | - Exchange and capability mappings 9 | - Main adapter for integration with OpenAlgo WebSocket proxy 10 | """ 11 | 12 | from .aliceblue_adapter import AliceblueWebSocketAdapter 13 | from .aliceblue_client import Aliceblue, Instrument, TransactionType, LiveFeedType, OrderType, ProductType 14 | from .aliceblue_mapping import ( 15 | AliceBlueExchangeMapper, 16 | AliceBlueCapabilityRegistry, 17 | AliceBlueMessageMapper, 18 | AliceBlueFeedType 19 | ) 20 | 21 | __all__ = [ 22 | 'AliceblueWebSocketAdapter', 23 | 'Aliceblue', 24 | 'Instrument', 25 | 'TransactionType', 26 | 'LiveFeedType', 27 | 'OrderType', 28 | 'ProductType', 29 | 'AliceBlueExchangeMapper', 30 | 'AliceBlueCapabilityRegistry', 31 | 'AliceBlueMessageMapper', 32 | 'AliceBlueFeedType' 33 | ] 34 | -------------------------------------------------------------------------------- /examples/python/depth_example.py: -------------------------------------------------------------------------------- 1 | """ 2 | OpenAlgo WebSocket Market Depth Example 3 | """ 4 | 5 | from openalgo import api 6 | import time 7 | 8 | # Initialize feed client with explicit parameters 9 | client = api( 10 | api_key="7653f710c940cdf1d757b5a7d808a60f43bc7e9c0239065435861da2869ec0fc", # Replace with your API key 11 | host="http://127.0.0.1:5000", # Replace with your API host 12 | ws_url="ws://127.0.0.1:8765" # Explicit WebSocket URL (can be different from REST API host) 13 | ) 14 | 15 | # MCX instruments for testing 16 | instruments_list = [ 17 | 18 | {"exchange": "NSE", "symbol": "TCS"} 19 | ] 20 | 21 | def on_data_received(data): 22 | print("Market Depth Update:") 23 | print(data) 24 | 25 | # Connect and subscribe 26 | client.connect() 27 | client.subscribe_depth(instruments_list, on_data_received=on_data_received) 28 | 29 | # Poll Market Depth data a few times 30 | for i in range(100): 31 | print(f"\nPoll {i+1}:") 32 | print(client.get_depth()) 33 | time.sleep(0.5) 34 | 35 | # Cleanup 36 | client.unsubscribe_depth(instruments_list) 37 | client.disconnect() -------------------------------------------------------------------------------- /examples/python/ltp_example.py: -------------------------------------------------------------------------------- 1 | """ 2 | OpenAlgo WebSocket Feed Example 3 | """ 4 | 5 | from openalgo import api 6 | import time 7 | 8 | # Initialize feed client with explicit parameters 9 | client = api( 10 | api_key="7653f710c940cdf1d757b5a7d808a60f43bc7e9c0239065435861da2869ec0fc", # Replace with your API key 11 | host="http://127.0.0.1:5000", # Replace with your API host 12 | ws_url="ws://127.0.0.1:8765" # Explicit WebSocket URL (can be different from REST API host) 13 | ) 14 | 15 | # MCX instruments for testing 16 | instruments_list = [ 17 | {"exchange": "NSE", "symbol": "TCS", 18 | "exchange": "NSE", "symbol": "INFY" 19 | } 20 | ] 21 | 22 | def on_data_received(data): 23 | print("LTP Update:") 24 | print(data) 25 | 26 | # Connect and subscribe 27 | client.connect() 28 | client.subscribe_ltp(instruments_list, on_data_received=on_data_received) 29 | 30 | # Poll LTP data a few times 31 | for i in range(100): 32 | print(f"\nPoll {i+1}:") 33 | print(client.get_ltp()) 34 | time.sleep(0.5) 35 | 36 | # Cleanup 37 | client.unsubscribe_ltp(instruments_list) 38 | client.disconnect() -------------------------------------------------------------------------------- /broker/iifl/mapping/margin_data.py: -------------------------------------------------------------------------------- 1 | # Mapping OpenAlgo API Request https://openalgo.in/docs 2 | # IIFL does not provide Margin Calculator API 3 | 4 | from utils.logging import get_logger 5 | 6 | logger = get_logger(__name__) 7 | 8 | def transform_margin_positions(positions): 9 | """ 10 | Transform OpenAlgo margin position format to broker format. 11 | 12 | Note: IIFL does not provide a margin calculator API. 13 | 14 | Args: 15 | positions: List of positions in OpenAlgo format 16 | 17 | Raises: 18 | NotImplementedError: IIFL does not support margin calculator API 19 | """ 20 | raise NotImplementedError("IIFL does not support margin calculator API") 21 | 22 | def parse_margin_response(response_data): 23 | """ 24 | Parse broker margin calculator response to OpenAlgo standard format. 25 | 26 | Note: IIFL does not provide a margin calculator API. 27 | 28 | Args: 29 | response_data: Raw response from broker margin calculator API 30 | 31 | Raises: 32 | NotImplementedError: IIFL does not support margin calculator API 33 | """ 34 | raise NotImplementedError("IIFL does not support margin calculator API") 35 | -------------------------------------------------------------------------------- /examples/python/quote_example.py: -------------------------------------------------------------------------------- 1 | """ 2 | OpenAlgo WebSocket Quote Feed Example 3 | """ 4 | 5 | from openalgo import api 6 | import time 7 | 8 | # Initialize feed client with explicit parameters 9 | client = api( 10 | api_key="7653f710c940cdf1d757b5a7d808a60f43bc7e9c0239065435861da2869ec0fc", # Replace with your API key 11 | host="http://127.0.0.1:5000", # Replace with your API host 12 | ws_url="ws://127.0.0.1:8765" # Explicit WebSocket URL (can be different from REST API host) 13 | ) 14 | 15 | # MCX instruments for testing 16 | instruments_list = [ 17 | {"exchange": "NSE_INDEX", "symbol": "NIFTY"}, 18 | {"exchange": "NSE", "symbol": "INFY"}, 19 | {"exchange": "NSE", "symbol": "TCS"} 20 | ] 21 | 22 | def on_data_received(data): 23 | print("Quote Update:") 24 | print(data) 25 | 26 | # Connect and subscribe 27 | client.connect() 28 | client.subscribe_quote(instruments_list, on_data_received=on_data_received) 29 | 30 | # Poll Quote data a few times 31 | for i in range(100): 32 | print(f"\nPoll {i+1}:") 33 | print(client.get_quotes()) 34 | time.sleep(0.5) 35 | 36 | # Cleanup 37 | client.unsubscribe_quote(instruments_list) 38 | client.disconnect() -------------------------------------------------------------------------------- /broker/ibulls/mapping/margin_data.py: -------------------------------------------------------------------------------- 1 | # Mapping OpenAlgo API Request https://openalgo.in/docs 2 | # IBulls does not provide Margin Calculator API 3 | 4 | from utils.logging import get_logger 5 | 6 | logger = get_logger(__name__) 7 | 8 | def transform_margin_positions(positions): 9 | """ 10 | Transform OpenAlgo margin position format to broker format. 11 | 12 | Note: IBulls does not provide a margin calculator API. 13 | 14 | Args: 15 | positions: List of positions in OpenAlgo format 16 | 17 | Raises: 18 | NotImplementedError: IBulls does not support margin calculator API 19 | """ 20 | raise NotImplementedError("IBulls does not support margin calculator API") 21 | 22 | def parse_margin_response(response_data): 23 | """ 24 | Parse broker margin calculator response to OpenAlgo standard format. 25 | 26 | Note: IBulls does not provide a margin calculator API. 27 | 28 | Args: 29 | response_data: Raw response from broker margin calculator API 30 | 31 | Raises: 32 | NotImplementedError: IBulls does not support margin calculator API 33 | """ 34 | raise NotImplementedError("IBulls does not support margin calculator API") 35 | -------------------------------------------------------------------------------- /broker/wisdom/mapping/margin_data.py: -------------------------------------------------------------------------------- 1 | # Mapping OpenAlgo API Request https://openalgo.in/docs 2 | # Wisdom does not provide Margin Calculator API 3 | 4 | from utils.logging import get_logger 5 | 6 | logger = get_logger(__name__) 7 | 8 | def transform_margin_positions(positions): 9 | """ 10 | Transform OpenAlgo margin position format to broker format. 11 | 12 | Note: Wisdom does not provide a margin calculator API. 13 | 14 | Args: 15 | positions: List of positions in OpenAlgo format 16 | 17 | Raises: 18 | NotImplementedError: Wisdom does not support margin calculator API 19 | """ 20 | raise NotImplementedError("Wisdom does not support margin calculator API") 21 | 22 | def parse_margin_response(response_data): 23 | """ 24 | Parse broker margin calculator response to OpenAlgo standard format. 25 | 26 | Note: Wisdom does not provide a margin calculator API. 27 | 28 | Args: 29 | response_data: Raw response from broker margin calculator API 30 | 31 | Raises: 32 | NotImplementedError: Wisdom does not support margin calculator API 33 | """ 34 | raise NotImplementedError("Wisdom does not support margin calculator API") 35 | -------------------------------------------------------------------------------- /.ebextensions/01_flask.config: -------------------------------------------------------------------------------- 1 | option_settings: 2 | aws:elasticbeanstalk:container:python: 3 | WSGIPath: "app:app" 4 | aws:elasticbeanstalk:application:environment: 5 | PYTHONPATH: "/var/app/current:$PYTHONPATH" 6 | 7 | container_commands: 8 | 01_create_directories: 9 | command: | 10 | mkdir -p /var/app/current/strategies/scripts 11 | mkdir -p /var/app/current/log/strategies 12 | mkdir -p /var/app/current/keys 13 | mkdir -p /var/app/current/db 14 | mkdir -p /var/app/current/tmp 15 | 02_set_permissions: 16 | command: | 17 | chmod 755 /var/app/current/strategies 18 | chmod 755 /var/app/current/strategies/scripts 19 | chmod 755 /var/app/current/log 20 | chmod 755 /var/app/current/log/strategies 21 | chmod 700 /var/app/current/keys 22 | chmod 755 /var/app/current/db 23 | chmod 755 /var/app/current/tmp 24 | 03_set_ownership: 25 | command: | 26 | chown -R webapp:webapp /var/app/current/strategies 27 | chown -R webapp:webapp /var/app/current/log 28 | chown -R webapp:webapp /var/app/current/keys 29 | chown -R webapp:webapp /var/app/current/db 30 | chown -R webapp:webapp /var/app/current/tmp 31 | 32 | -------------------------------------------------------------------------------- /broker/aliceblue/mapping/margin_data.py: -------------------------------------------------------------------------------- 1 | # Mapping OpenAlgo API Request https://openalgo.in/docs 2 | # AliceBlue does not provide Margin Calculator API 3 | 4 | from utils.logging import get_logger 5 | 6 | logger = get_logger(__name__) 7 | 8 | def transform_margin_positions(positions): 9 | """ 10 | Transform OpenAlgo margin position format to broker format. 11 | 12 | Note: AliceBlue does not provide a margin calculator API. 13 | 14 | Args: 15 | positions: List of positions in OpenAlgo format 16 | 17 | Raises: 18 | NotImplementedError: AliceBlue does not support margin calculator API 19 | """ 20 | raise NotImplementedError("AliceBlue does not support margin calculator API") 21 | 22 | def parse_margin_response(response_data): 23 | """ 24 | Parse broker margin calculator response to OpenAlgo standard format. 25 | 26 | Note: AliceBlue does not provide a margin calculator API. 27 | 28 | Args: 29 | response_data: Raw response from broker margin calculator API 30 | 31 | Raises: 32 | NotImplementedError: AliceBlue does not support margin calculator API 33 | """ 34 | raise NotImplementedError("AliceBlue does not support margin calculator API") 35 | -------------------------------------------------------------------------------- /broker/jainamxts/mapping/margin_data.py: -------------------------------------------------------------------------------- 1 | # Mapping OpenAlgo API Request https://openalgo.in/docs 2 | # JainamXTS does not provide Margin Calculator API 3 | 4 | from utils.logging import get_logger 5 | 6 | logger = get_logger(__name__) 7 | 8 | def transform_margin_positions(positions): 9 | """ 10 | Transform OpenAlgo margin position format to broker format. 11 | 12 | Note: JainamXTS does not provide a margin calculator API. 13 | 14 | Args: 15 | positions: List of positions in OpenAlgo format 16 | 17 | Raises: 18 | NotImplementedError: JainamXTS does not support margin calculator API 19 | """ 20 | raise NotImplementedError("JainamXTS does not support margin calculator API") 21 | 22 | def parse_margin_response(response_data): 23 | """ 24 | Parse broker margin calculator response to OpenAlgo standard format. 25 | 26 | Note: JainamXTS does not provide a margin calculator API. 27 | 28 | Args: 29 | response_data: Raw response from broker margin calculator API 30 | 31 | Raises: 32 | NotImplementedError: JainamXTS does not support margin calculator API 33 | """ 34 | raise NotImplementedError("JainamXTS does not support margin calculator API") 35 | -------------------------------------------------------------------------------- /broker/pocketful/mapping/margin_data.py: -------------------------------------------------------------------------------- 1 | # Mapping OpenAlgo API Request https://openalgo.in/docs 2 | # Pocketful does not provide Margin Calculator API 3 | 4 | from utils.logging import get_logger 5 | 6 | logger = get_logger(__name__) 7 | 8 | def transform_margin_positions(positions): 9 | """ 10 | Transform OpenAlgo margin position format to broker format. 11 | 12 | Note: Pocketful does not provide a margin calculator API. 13 | 14 | Args: 15 | positions: List of positions in OpenAlgo format 16 | 17 | Raises: 18 | NotImplementedError: Pocketful does not support margin calculator API 19 | """ 20 | raise NotImplementedError("Pocketful does not support margin calculator API") 21 | 22 | def parse_margin_response(response_data): 23 | """ 24 | Parse broker margin calculator response to OpenAlgo standard format. 25 | 26 | Note: Pocketful does not provide a margin calculator API. 27 | 28 | Args: 29 | response_data: Raw response from broker margin calculator API 30 | 31 | Raises: 32 | NotImplementedError: Pocketful does not support margin calculator API 33 | """ 34 | raise NotImplementedError("Pocketful does not support margin calculator API") 35 | -------------------------------------------------------------------------------- /broker/definedge/mapping/symbol_map.py: -------------------------------------------------------------------------------- 1 | from utils.logging import get_logger 2 | 3 | logger = get_logger(__name__) 4 | 5 | def get_br_symbol(symbol, exchange): 6 | """Convert OpenAlgo symbol to DefinedGe Securities symbol format""" 7 | try: 8 | # DefinedGe uses similar symbol format to NSE/BSE 9 | # For equity symbols, remove -EQ suffix if present 10 | if exchange in ['NSE', 'BSE'] and symbol.endswith('-EQ'): 11 | return symbol[:-3] 12 | 13 | # For derivatives, DefinedGe uses standard format 14 | return symbol 15 | 16 | except Exception as e: 17 | logger.error(f"Error converting symbol {symbol}: {e}") 18 | return symbol 19 | 20 | def get_oa_symbol(symbol, exchange): 21 | """Convert DefinedGe Securities symbol to OpenAlgo symbol format""" 22 | try: 23 | # For equity symbols on NSE, add -EQ suffix 24 | if exchange == 'NSE' and not any(x in symbol for x in ['FUT', 'CE', 'PE']): 25 | return f"{symbol}-EQ" 26 | 27 | # For other exchanges and derivatives, return as is 28 | return symbol 29 | 30 | except Exception as e: 31 | logger.error(f"Error converting symbol {symbol}: {e}") 32 | return symbol 33 | -------------------------------------------------------------------------------- /broker/compositedge/mapping/margin_data.py: -------------------------------------------------------------------------------- 1 | # Mapping OpenAlgo API Request https://openalgo.in/docs 2 | # Compositedge does not provide Margin Calculator API 3 | 4 | from utils.logging import get_logger 5 | 6 | logger = get_logger(__name__) 7 | 8 | def transform_margin_positions(positions): 9 | """ 10 | Transform OpenAlgo margin position format to broker format. 11 | 12 | Note: Compositedge does not provide a margin calculator API. 13 | 14 | Args: 15 | positions: List of positions in OpenAlgo format 16 | 17 | Raises: 18 | NotImplementedError: Compositedge does not support margin calculator API 19 | """ 20 | raise NotImplementedError("Compositedge does not support margin calculator API") 21 | 22 | def parse_margin_response(response_data): 23 | """ 24 | Parse broker margin calculator response to OpenAlgo standard format. 25 | 26 | Note: Compositedge does not provide a margin calculator API. 27 | 28 | Args: 29 | response_data: Raw response from broker margin calculator API 30 | 31 | Raises: 32 | NotImplementedError: Compositedge does not support margin calculator API 33 | """ 34 | raise NotImplementedError("Compositedge does not support margin calculator API") 35 | -------------------------------------------------------------------------------- /broker/fivepaisaxts/mapping/margin_data.py: -------------------------------------------------------------------------------- 1 | # Mapping OpenAlgo API Request https://openalgo.in/docs 2 | # FivePaisaXTS does not provide Margin Calculator API 3 | 4 | from utils.logging import get_logger 5 | 6 | logger = get_logger(__name__) 7 | 8 | def transform_margin_positions(positions): 9 | """ 10 | Transform OpenAlgo margin position format to broker format. 11 | 12 | Note: FivePaisaXTS does not provide a margin calculator API. 13 | 14 | Args: 15 | positions: List of positions in OpenAlgo format 16 | 17 | Raises: 18 | NotImplementedError: FivePaisaXTS does not support margin calculator API 19 | """ 20 | raise NotImplementedError("FivePaisaXTS does not support margin calculator API") 21 | 22 | def parse_margin_response(response_data): 23 | """ 24 | Parse broker margin calculator response to OpenAlgo standard format. 25 | 26 | Note: FivePaisaXTS does not provide a margin calculator API. 27 | 28 | Args: 29 | response_data: Raw response from broker margin calculator API 30 | 31 | Raises: 32 | NotImplementedError: FivePaisaXTS does not support margin calculator API 33 | """ 34 | raise NotImplementedError("FivePaisaXTS does not support margin calculator API") 35 | -------------------------------------------------------------------------------- /broker/mstock/remainwork.md: -------------------------------------------------------------------------------- 1 | # Remaining Work for MStock Broker Integration 2 | 3 | ## Missing Files 4 | 5 | - `broker/mstock/api/__init__.py` ok 6 | - `broker/mstock/streaming/mstock_mapping.py` 7 | - `broker/mstock/streaming/smartWebSocketV2.py` (or equivalent) 8 | 9 | ## Missing Functions 10 | 11 | ### `broker/mstock/api/data.py` 12 | 13 | - `get_quotes(symbol, exchange)` 14 | - `get_history(symbol, exchange, interval, start_date, end_date)` 15 | - `get_depth(symbol, exchange)` 16 | 17 | ### `broker/mstock/api/order_api.py` 18 | 19 | - `get_positions(auth)` ok 20 | - `get_holdings(auth)` ok 21 | - `get_open_position(tradingsymbol, exchange, producttype,auth)` 22 | - `place_smartorder_api(data,auth)` 23 | - `close_all_positions(current_api_key,auth)` 24 | - `cancel_all_orders_api(data,auth)` 25 | 26 | ### `broker/mstock/mapping/order_data.py` 27 | 28 | - `map_order_data(order_data)` 29 | - `calculate_order_statistics(order_data)` 30 | - `map_trade_data(trade_data)` 31 | - `map_position_data(position_data)` 32 | - `map_portfolio_data(portfolio_data)` 33 | - `calculate_portfolio_statistics(holdings_data)` 34 | 35 | ### `broker/mstock/mapping/transform_data.py` 36 | 37 | - `map_product_type(product)` 38 | - `reverse_map_product_type(product)` 39 | - `map_variety(pricetype)` 40 | -------------------------------------------------------------------------------- /docker-compose.yaml: -------------------------------------------------------------------------------- 1 | services: 2 | openalgo: 3 | image: openalgo:latest 4 | build: 5 | context: . 6 | dockerfile: Dockerfile 7 | 8 | container_name: openalgo-web 9 | ports: 10 | - "${FLASK_PORT:-5000}:5000" 11 | - "${WEBSOCKET_PORT:-8765}:8765" 12 | 13 | # persistent DB, logs + mount the host .env read-only so dotenv can read it 14 | volumes: 15 | - openalgo_db:/app/db 16 | - openalgo_logs:/app/logs # Use named volume for logs 17 | - openalgo_log:/app/log # Application logs (named volume) 18 | - openalgo_strategies:/app/strategies # Python strategies (named volume - fixes permission issues) 19 | - openalgo_keys:/app/keys # API keys/certificates (named volume) 20 | - ./.env:/app/.env:ro 21 | 22 | # (optional) extra env-vars that are NOT in .env 23 | environment: 24 | - FLASK_ENV=${FLASK_ENV:-production} 25 | - FLASK_DEBUG=${FLASK_DEBUG:-0} 26 | 27 | restart: unless-stopped 28 | 29 | # Define named volumes for database and logs persistence 30 | volumes: 31 | openalgo_db: 32 | driver: local 33 | openalgo_logs: 34 | driver: local 35 | openalgo_log: 36 | driver: local 37 | openalgo_strategies: 38 | driver: local 39 | openalgo_keys: 40 | driver: local 41 | -------------------------------------------------------------------------------- /broker/motilal/mapping/margin_data.py: -------------------------------------------------------------------------------- 1 | # Mapping OpenAlgo API Request https://openalgo.in/docs 2 | # Mapping Motilal Oswal Margin API - See Motilal_Oswal.md documentation 3 | # Note: Motilal Oswal does not provide a margin calculator API. 4 | 5 | from utils.logging import get_logger 6 | 7 | logger = get_logger(__name__) 8 | 9 | 10 | def transform_margin_positions(positions): 11 | """ 12 | Transform OpenAlgo margin position format to Motilal Oswal margin format. 13 | 14 | Note: Motilal Oswal does not provide a margin calculator API. 15 | This function is a placeholder for API consistency. 16 | 17 | Args: 18 | positions: List of positions in OpenAlgo format 19 | 20 | Returns: 21 | Empty list (API not supported) 22 | """ 23 | logger.warning("Motilal Oswal does not provide margin calculator API") 24 | return [] 25 | 26 | 27 | def parse_margin_response(response_data): 28 | """ 29 | Parse margin response. 30 | 31 | Note: Motilal Oswal does not provide a margin calculator API. 32 | This function is a placeholder for API consistency. 33 | 34 | Args: 35 | response_data: Response data 36 | 37 | Returns: 38 | Error dict (API not supported) 39 | """ 40 | return { 41 | 'status': 'error', 42 | 'message': 'Motilal Oswal does not support margin calculator API' 43 | } 44 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "openalgo", 3 | "version": "1.0.0", 4 | "type": "module", 5 | "description": "OpenAlgo is an open-source Flask-based Python application designed to bridge the gap between traders and major trading platforms such as Amibroker, Tradingview, Excel, and Google Spreadsheets. With a focus on simplifying algotrading, OpenAlgo facilitates easy integration, automation, and execution of trading strategies, providing a user-friendly interface to enhance trading performance.", 6 | "main": "index.js", 7 | "scripts": { 8 | "build:css": "cross-env NODE_ENV=production postcss src/css/styles.css -o static/css/main.css", 9 | "watch:css": "postcss src/css/styles.css -o static/css/main.css --watch", 10 | "build": "cross-env NODE_ENV=production postcss src/css/styles.css -o static/css/main.css", 11 | "dev": "postcss src/css/styles.css -o static/css/main.css --watch" 12 | }, 13 | "keywords": [], 14 | "author": "", 15 | "license": "ISC", 16 | "devDependencies": { 17 | "@tailwindcss/postcss": "^4.1.14", 18 | "autoprefixer": "^10.4.20", 19 | "cross-env": "^10.1.0", 20 | "cssnano": "^7.1.2", 21 | "daisyui": "^5.1.27", 22 | "postcss": "^8.4.49", 23 | "postcss-cli": "^11.0.0", 24 | "postcss-nesting": "^13.0.1", 25 | "tailwindcss": "^4.1.14" 26 | }, 27 | "dependencies": { 28 | "chart.js": "^4.5.1" 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /test/test_bot_web.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | """ 3 | Test script to verify bot starts properly from web UI 4 | """ 5 | import time 6 | from services.telegram_bot_service import init_bot_sync, start_bot_sync, stop_bot_sync, get_telegram_bot 7 | from database.telegram_db import get_bot_config 8 | 9 | # Get config 10 | config = get_bot_config() 11 | 12 | if not config.get('token'): 13 | print("[ERROR] No bot token configured") 14 | exit(1) 15 | 16 | print(f"[INFO] Bot token found: {config['token'][:10]}...") 17 | 18 | # Initialize bot 19 | print("[INFO] Initializing bot...") 20 | success, message = init_bot_sync(config['token'], None) 21 | 22 | if not success: 23 | print(f"[ERROR] Failed to initialize: {message}") 24 | exit(1) 25 | 26 | print(f"[OK] {message}") 27 | 28 | # Start bot 29 | print("[INFO] Starting bot in polling mode...") 30 | success, message = start_bot_sync() 31 | 32 | if not success: 33 | print(f"[ERROR] Failed to start: {message}") 34 | exit(1) 35 | 36 | print(f"[OK] {message}") 37 | 38 | # Check status 39 | bot = get_telegram_bot() 40 | print(f"[STATUS] Bot running: {bot.is_running}") 41 | 42 | # Keep running for testing 43 | print("[INFO] Bot is running. Press Ctrl+C to stop.") 44 | try: 45 | while True: 46 | time.sleep(1) 47 | except KeyboardInterrupt: 48 | print("\n[INFO] Stopping bot...") 49 | success, message = stop_bot_sync() 50 | print(f"[OK] {message}") -------------------------------------------------------------------------------- /utils/plugin_loader.py: -------------------------------------------------------------------------------- 1 | # utils/plugin_loader.py 2 | 3 | import os 4 | import importlib 5 | from flask import current_app 6 | from utils.logging import get_logger 7 | 8 | logger = get_logger(__name__) 9 | 10 | def load_broker_auth_functions(broker_directory='broker'): 11 | auth_functions = {} 12 | broker_path = os.path.join(current_app.root_path, broker_directory) 13 | # List all items in broker directory and filter out __pycache__ and non-directories 14 | broker_names = [d for d in os.listdir(broker_path) 15 | if os.path.isdir(os.path.join(broker_path, d)) and d != '__pycache__'] 16 | 17 | for broker_name in broker_names: 18 | try: 19 | # Construct module name and import the module 20 | module_name = f"{broker_directory}.{broker_name}.api.auth_api" 21 | auth_module = importlib.import_module(module_name) 22 | # Retrieve the authenticate_broker function 23 | auth_function = getattr(auth_module, 'authenticate_broker', None) 24 | if auth_function: 25 | auth_functions[f"{broker_name}_auth"] = auth_function 26 | except ImportError as e: 27 | logger.error(f"Failed to import broker plugin {broker_name}: {e}") 28 | except AttributeError as e: 29 | logger.error(f"Authentication function not found in broker plugin {broker_name}: {e}") 30 | 31 | return auth_functions 32 | -------------------------------------------------------------------------------- /examples/python/placing ATM order.py: -------------------------------------------------------------------------------- 1 | from openalgo import api 2 | 3 | print("🔁 OpenAlgo Python Bot is running.") 4 | 5 | # ------------------------------------------ 6 | # Initialize API client 7 | # ------------------------------------------ 8 | client = api( 9 | api_key="83ad96143dd5081d033abcfd20e9108daee5708fbea404121a762bed1e498dd0", 10 | host="http://127.0.0.1:5000" 11 | ) 12 | 13 | # ------------------------------------------ 14 | # Fetch NIFTY Spot (must print immediately) 15 | # ------------------------------------------ 16 | quote = client.quotes(symbol="NIFTY", exchange="NSE_INDEX") 17 | print("NIFTY QUOTE:", quote) 18 | 19 | # ------------------------------------------ 20 | # Place NIFTY ATM Option Order - 09DEC25 21 | # ------------------------------------------ 22 | response = client.optionsorder( 23 | strategy="python", 24 | underlying="NIFTY", # Underlying Index 25 | exchange="NSE_INDEX", # Index exchange 26 | expiry_date="09DEC25", # Correct expiry 27 | offset="OTM2", # Auto-select ATM strike 28 | option_type="CE", # CE or PE 29 | action="BUY", # BUY or SELL 30 | quantity=75, # 1 Lot = 75 31 | pricetype="MARKET", # MARKET or LIMIT 32 | product="NRML", # NRML or MIS 33 | splitsize=0 # 0 = no split 34 | ) 35 | 36 | print("ORDER RESPONSE:", response) 37 | -------------------------------------------------------------------------------- /broker/zebu/mapping/margin_data.py: -------------------------------------------------------------------------------- 1 | # Mapping OpenAlgo API Request https://openalgo.in/docs 2 | # Zebu does not provide position-specific Margin Calculator API 3 | 4 | from utils.logging import get_logger 5 | 6 | logger = get_logger(__name__) 7 | 8 | def transform_margin_positions(positions): 9 | """ 10 | Transform OpenAlgo margin position format to broker format. 11 | 12 | Note: Zebu does not provide a position-specific margin calculator API. 13 | The available Margin API only returns account-level margin information. 14 | 15 | Args: 16 | positions: List of positions in OpenAlgo format 17 | 18 | Raises: 19 | NotImplementedError: Zebu does not support position-specific margin calculator API 20 | """ 21 | raise NotImplementedError("Zebu does not support position-specific margin calculator API") 22 | 23 | def parse_margin_response(response_data): 24 | """ 25 | Parse broker margin calculator response to OpenAlgo standard format. 26 | 27 | Note: Zebu does not provide a position-specific margin calculator API. 28 | The available Margin API only returns account-level margin information. 29 | 30 | Args: 31 | response_data: Raw response from broker margin calculator API 32 | 33 | Raises: 34 | NotImplementedError: Zebu does not support position-specific margin calculator API 35 | """ 36 | raise NotImplementedError("Zebu does not support position-specific margin calculator API") 37 | -------------------------------------------------------------------------------- /collections/openalgo/optionsmultiorder.bru: -------------------------------------------------------------------------------- 1 | meta { 2 | name: Options Multi Order 3 | type: http 4 | seq: 6 5 | } 6 | 7 | post { 8 | url: http://127.0.0.1:5000/api/v1/optionsmultiorder 9 | body: json 10 | auth: none 11 | } 12 | 13 | body:json { 14 | { 15 | "apikey": "", 16 | "strategy": "Iron Condor", 17 | "underlying": "NIFTY", 18 | "exchange": "NSE_INDEX", 19 | "legs": [ 20 | { 21 | "offset": "OTM10", 22 | "option_type": "CE", 23 | "action": "BUY", 24 | "quantity": 75, 25 | "expiry_date": "30DEC25", 26 | "splitsize": 0 27 | }, 28 | { 29 | "offset": "OTM10", 30 | "option_type": "PE", 31 | "action": "BUY", 32 | "quantity": 75, 33 | "expiry_date": "30DEC25", 34 | "splitsize": 0 35 | }, 36 | { 37 | "offset": "OTM5", 38 | "option_type": "CE", 39 | "action": "SELL", 40 | "quantity": 75, 41 | "expiry_date": "30DEC25", 42 | "splitsize": 0 43 | }, 44 | { 45 | "offset": "OTM5", 46 | "option_type": "PE", 47 | "action": "SELL", 48 | "quantity": 75, 49 | "expiry_date": "30DEC25", 50 | "splitsize": 0 51 | } 52 | ] 53 | } 54 | } 55 | -------------------------------------------------------------------------------- /broker/fivepaisa/mapping/margin_data.py: -------------------------------------------------------------------------------- 1 | # Mapping OpenAlgo API Request https://openalgo.in/docs 2 | # 5paisa does not provide position-specific Margin Calculator API 3 | 4 | from utils.logging import get_logger 5 | 6 | logger = get_logger(__name__) 7 | 8 | def transform_margin_positions(positions): 9 | """ 10 | Transform OpenAlgo margin position format to broker format. 11 | 12 | Note: 5paisa does not provide a position-specific margin calculator API. 13 | The available Margin API only returns account-level margin information. 14 | 15 | Args: 16 | positions: List of positions in OpenAlgo format 17 | 18 | Raises: 19 | NotImplementedError: 5paisa does not support position-specific margin calculator API 20 | """ 21 | raise NotImplementedError("5paisa does not support position-specific margin calculator API") 22 | 23 | def parse_margin_response(response_data): 24 | """ 25 | Parse broker margin calculator response to OpenAlgo standard format. 26 | 27 | Note: 5paisa does not provide a position-specific margin calculator API. 28 | The available Margin API only returns account-level margin information. 29 | 30 | Args: 31 | response_data: Raw response from broker margin calculator API 32 | 33 | Raises: 34 | NotImplementedError: 5paisa does not support position-specific margin calculator API 35 | """ 36 | raise NotImplementedError("5paisa does not support position-specific margin calculator API") 37 | -------------------------------------------------------------------------------- /restx_api/account_schema.py: -------------------------------------------------------------------------------- 1 | from marshmallow import Schema, fields, validate, INCLUDE 2 | 3 | class FundsSchema(Schema): 4 | apikey = fields.Str(required=True) 5 | 6 | class OrderbookSchema(Schema): 7 | apikey = fields.Str(required=True) 8 | 9 | class TradebookSchema(Schema): 10 | apikey = fields.Str(required=True) 11 | 12 | class PositionbookSchema(Schema): 13 | apikey = fields.Str(required=True) 14 | 15 | class HoldingsSchema(Schema): 16 | apikey = fields.Str(required=True) 17 | 18 | class OrderStatusSchema(Schema): 19 | apikey = fields.Str(required=True) 20 | strategy = fields.Str(required=True) 21 | orderid = fields.Str(required=True) 22 | 23 | class OpenPositionSchema(Schema): 24 | apikey = fields.Str(required=True) 25 | strategy = fields.Str(required=True) 26 | symbol = fields.Str(required=True) 27 | exchange = fields.Str(required=True) 28 | product = fields.Str(required=True, validate=validate.OneOf(["MIS", "NRML", "CNC"])) 29 | 30 | class AnalyzerSchema(Schema): 31 | apikey = fields.Str(required=True) 32 | 33 | class AnalyzerToggleSchema(Schema): 34 | apikey = fields.Str(required=True) 35 | mode = fields.Bool(required=True) 36 | 37 | class PingSchema(Schema): 38 | apikey = fields.Str(required=True) 39 | 40 | class ChartSchema(Schema): 41 | apikey = fields.Str(required=True) 42 | 43 | class Meta: 44 | # Allow unknown fields - chart preferences can have any key-value pairs 45 | unknown = INCLUDE 46 | -------------------------------------------------------------------------------- /broker/tradejini/mapping/margin_data.py: -------------------------------------------------------------------------------- 1 | # Mapping OpenAlgo API Request https://openalgo.in/docs 2 | # Tradejini does not provide position-specific Margin Calculator API 3 | 4 | from utils.logging import get_logger 5 | 6 | logger = get_logger(__name__) 7 | 8 | def transform_margin_positions(positions): 9 | """ 10 | Transform OpenAlgo margin position format to broker format. 11 | 12 | Note: Tradejini does not provide a position-specific margin calculator API. 13 | The available Margin API only returns account-level margin information. 14 | 15 | Args: 16 | positions: List of positions in OpenAlgo format 17 | 18 | Raises: 19 | NotImplementedError: Tradejini does not support position-specific margin calculator API 20 | """ 21 | raise NotImplementedError("Tradejini does not support position-specific margin calculator API") 22 | 23 | def parse_margin_response(response_data): 24 | """ 25 | Parse broker margin calculator response to OpenAlgo standard format. 26 | 27 | Note: Tradejini does not provide a position-specific margin calculator API. 28 | The available Margin API only returns account-level margin information. 29 | 30 | Args: 31 | response_data: Raw response from broker margin calculator API 32 | 33 | Raises: 34 | NotImplementedError: Tradejini does not support position-specific margin calculator API 35 | """ 36 | raise NotImplementedError("Tradejini does not support position-specific margin calculator API") 37 | -------------------------------------------------------------------------------- /test/test_history_format.py: -------------------------------------------------------------------------------- 1 | import sys 2 | import os 3 | # Add parent directory to path for imports 4 | sys.path.insert(0, os.path.dirname(os.path.dirname(os.path.abspath(__file__)))) 5 | 6 | from openalgo import api 7 | import pandas as pd 8 | from datetime import datetime, timedelta 9 | 10 | # Test the history API to see the response format 11 | client = api(api_key="56c3dc6ba7d9c9df478e4f19ffc5d3e15e1dd91b5aa11e91c910f202c91eff9d", host="http://127.0.0.1:5000") 12 | 13 | # Get history data 14 | end_date = datetime.now() 15 | start_date = end_date - timedelta(days=5) 16 | 17 | response = client.history( 18 | symbol="RELIANCE", 19 | exchange="NSE", 20 | interval="5m", 21 | start_date=start_date.strftime("%Y-%m-%d"), 22 | end_date=end_date.strftime("%Y-%m-%d") 23 | ) 24 | 25 | print("History API Response Type:", type(response)) 26 | 27 | if isinstance(response, pd.DataFrame): 28 | print("\nResponse is a DataFrame!") 29 | print("Shape:", response.shape) 30 | print("Columns:", list(response.columns)) 31 | print("\nFirst 5 rows:") 32 | print(response.head()) 33 | elif isinstance(response, dict): 34 | print("\nResponse is a dictionary with keys:", response.keys()) 35 | if 'data' in response: 36 | data = response['data'] 37 | print("Data type:", type(data)) 38 | if isinstance(data, pd.DataFrame): 39 | print("Data is a DataFrame with columns:", list(data.columns)) 40 | else: 41 | print("Unexpected response type:", type(response)) -------------------------------------------------------------------------------- /broker/paytm/mapping/margin_data.py: -------------------------------------------------------------------------------- 1 | # Mapping OpenAlgo API Request https://openalgo.in/docs 2 | # Paytm Money does not provide position-specific Margin Calculator API 3 | 4 | from utils.logging import get_logger 5 | 6 | logger = get_logger(__name__) 7 | 8 | def transform_margin_positions(positions): 9 | """ 10 | Transform OpenAlgo margin position format to broker format. 11 | 12 | Note: Paytm Money does not provide a position-specific margin calculator API. 13 | The available Margin API only returns account-level margin information. 14 | 15 | Args: 16 | positions: List of positions in OpenAlgo format 17 | 18 | Raises: 19 | NotImplementedError: Paytm Money does not support position-specific margin calculator API 20 | """ 21 | raise NotImplementedError("Paytm Money does not support position-specific margin calculator API") 22 | 23 | def parse_margin_response(response_data): 24 | """ 25 | Parse broker margin calculator response to OpenAlgo standard format. 26 | 27 | Note: Paytm Money does not provide a position-specific margin calculator API. 28 | The available Margin API only returns account-level margin information. 29 | 30 | Args: 31 | response_data: Raw response from broker margin calculator API 32 | 33 | Raises: 34 | NotImplementedError: Paytm Money does not support position-specific margin calculator API 35 | """ 36 | raise NotImplementedError("Paytm Money does not support position-specific margin calculator API") 37 | -------------------------------------------------------------------------------- /broker/dhan_sandbox/streaming/dhan_mapping.py: -------------------------------------------------------------------------------- 1 | """ 2 | Mapping utilities for Dhan broker integration. 3 | Provides exchange code mappings between OpenAlgo and Dhan formats. 4 | """ 5 | from typing import Dict 6 | 7 | # Exchange code mappings 8 | # OpenAlgo exchange code -> Dhan exchange code 9 | OPENALGO_TO_DHAN_EXCHANGE = { 10 | "NSE": "NSE_EQ", 11 | "BSE": "BSE_EQ", 12 | "NFO": "NSE_FNO", 13 | "BFO": "BSE_FNO", 14 | "CDS": "NSE_CURRENCY", 15 | "BCD": "BSE_CURRENCY", 16 | "MCX": "MCX_COMM", 17 | "NSE_INDEX": "IDX_I", 18 | "BSE_INDEX": "IDX_I" 19 | } 20 | 21 | # Dhan exchange code -> OpenAlgo exchange code 22 | DHAN_TO_OPENALGO_EXCHANGE = {v: k for k, v in OPENALGO_TO_DHAN_EXCHANGE.items()} 23 | 24 | def get_dhan_exchange(openalgo_exchange: str) -> str: 25 | """ 26 | Convert OpenAlgo exchange code to Dhan exchange code. 27 | 28 | Args: 29 | openalgo_exchange (str): Exchange code in OpenAlgo format 30 | 31 | Returns: 32 | str: Exchange code in Dhan format 33 | """ 34 | return OPENALGO_TO_DHAN_EXCHANGE.get(openalgo_exchange, openalgo_exchange) 35 | 36 | def get_openalgo_exchange(dhan_exchange: str) -> str: 37 | """ 38 | Convert Dhan exchange code to OpenAlgo exchange code. 39 | 40 | Args: 41 | dhan_exchange (str): Exchange code in Dhan format 42 | 43 | Returns: 44 | str: Exchange code in OpenAlgo format 45 | """ 46 | return DHAN_TO_OPENALGO_EXCHANGE.get(dhan_exchange, dhan_exchange) 47 | -------------------------------------------------------------------------------- /static/js/app.js: -------------------------------------------------------------------------------- 1 | document.addEventListener('DOMContentLoaded', function() { 2 | const regenerateKeyBtn = document.getElementById('regenerateKey'); 3 | if (regenerateKeyBtn) { 4 | regenerateKeyBtn.addEventListener('click', function() { 5 | regenerateApiKey(); 6 | }); 7 | } 8 | }); 9 | 10 | function regenerateApiKey() { 11 | // Example user ID - replace with actual logic to retrieve the user ID, if necessary 12 | const userInfo = document.getElementById('userInfo'); 13 | if (!userInfo) return; 14 | 15 | const userId = userInfo.getAttribute('data-login-username'); 16 | 17 | // Use fetchWithCSRF if available, otherwise fallback to regular fetch with CSRF header 18 | const csrfToken = typeof getCSRFToken === 'function' ? getCSRFToken() : ''; 19 | 20 | fetch('/apikey', { 21 | method: 'POST', 22 | headers: { 23 | 'Content-Type': 'application/json', 24 | 'X-CSRFToken': csrfToken 25 | }, 26 | body: JSON.stringify({ user_id: userId }) 27 | }) 28 | .then(response => { 29 | if (!response.ok) { 30 | throw new Error('Network response was not ok'); 31 | } 32 | return response.json(); 33 | }) 34 | .then(data => { 35 | const apiKeyDisplay = document.getElementById('apiKeyDisplay'); 36 | if (apiKeyDisplay) { 37 | apiKeyDisplay.textContent = data.api_key; 38 | } 39 | }) 40 | .catch((error) => { 41 | console.error('Error:', error); 42 | }); 43 | } 44 | -------------------------------------------------------------------------------- /broker/firstock/streaming/firstock_mapping.py: -------------------------------------------------------------------------------- 1 | """ 2 | Firstock-specific exchange mapping and capability registry 3 | """ 4 | 5 | class FirstockExchangeMapper: 6 | """Maps between standard exchange codes and Firstock-specific codes""" 7 | 8 | # Mapping from standard codes to Firstock exchange codes 9 | EXCHANGE_MAP = { 10 | 'NSE': 'NSE', 11 | 'BSE': 'BSE', 12 | 'NFO': 'NFO', 13 | 'CDS': 'CDS', 14 | 'MCX': 'MCX', 15 | 'BFO': 'BFO', 16 | 'NSE_INDEX': 'NSE' # NSE indices use NSE exchange in Firstock 17 | } 18 | 19 | # Reverse mapping 20 | REVERSE_MAP = {v: k for k, v in EXCHANGE_MAP.items()} 21 | 22 | @classmethod 23 | def get_firstock_exchange(cls, standard_exchange: str) -> str: 24 | """ 25 | Convert standard exchange code to Firstock-specific code 26 | 27 | Args: 28 | standard_exchange: Standard exchange code (e.g., 'NSE') 29 | 30 | Returns: 31 | str: Firstock exchange code 32 | """ 33 | return cls.EXCHANGE_MAP.get(standard_exchange, standard_exchange) 34 | 35 | @classmethod 36 | def get_standard_exchange(cls, firstock_exchange: str) -> str: 37 | """ 38 | Convert Firstock exchange code to standard code 39 | 40 | Args: 41 | firstock_exchange: Firstock exchange code 42 | 43 | Returns: 44 | str: Standard exchange code 45 | """ 46 | return cls.REVERSE_MAP.get(firstock_exchange, firstock_exchange) -------------------------------------------------------------------------------- /database/db_init_helper.py: -------------------------------------------------------------------------------- 1 | """ 2 | Helper module for database initialization with better logging 3 | """ 4 | from sqlalchemy import inspect 5 | 6 | 7 | def init_db_with_logging(base, engine, db_name, logger): 8 | """ 9 | Initialize database tables with detailed logging 10 | 11 | Args: 12 | base: SQLAlchemy Base (declarative_base) 13 | engine: SQLAlchemy engine 14 | db_name: Name of the database (for logging) 15 | logger: Logger instance 16 | 17 | Returns: 18 | tuple: (tables_created, tables_verified) 19 | """ 20 | # Get inspector to check existing tables 21 | inspector = inspect(engine) 22 | existing_tables = set(inspector.get_table_names()) 23 | 24 | # Get tables defined in this model 25 | model_tables = set(base.metadata.tables.keys()) 26 | 27 | # Find which tables need to be created 28 | tables_to_create = model_tables - existing_tables 29 | tables_already_exist = model_tables & existing_tables 30 | 31 | # Create tables (only creates missing ones) 32 | base.metadata.create_all(bind=engine) 33 | 34 | # Log appropriately 35 | if tables_to_create: 36 | logger.debug(f"{db_name}: Created {len(tables_to_create)} new table(s): {', '.join(sorted(tables_to_create))}") 37 | 38 | if tables_already_exist: 39 | logger.debug(f"{db_name}: Verified {len(tables_already_exist)} existing table(s)") 40 | 41 | if not tables_to_create and tables_already_exist: 42 | logger.debug(f"{db_name}: Connection verified ({len(tables_already_exist)} table(s) ready)") 43 | 44 | return len(tables_to_create), len(tables_already_exist) 45 | -------------------------------------------------------------------------------- /broker/dhan_sandbox/api/auth_api.py: -------------------------------------------------------------------------------- 1 | import httpx 2 | import json 3 | import os 4 | from utils.httpx_client import get_httpx_client 5 | from broker.dhan_sandbox.api.baseurl import get_url, BASE_URL 6 | 7 | 8 | 9 | def authenticate_broker(code): 10 | try: 11 | BROKER_API_KEY = os.getenv('BROKER_API_KEY') 12 | BROKER_API_SECRET = os.getenv('BROKER_API_SECRET') 13 | REDIRECT_URL = os.getenv('REDIRECT_URL') 14 | 15 | # Get the shared httpx client with connection pooling 16 | client = get_httpx_client() 17 | 18 | # Your authentication implementation here 19 | # For now, returning API secret as a placeholder like the original code 20 | return BROKER_API_SECRET, None 21 | 22 | if response.status_code == 200: 23 | response_data = response.json() 24 | if 'access_token' in response_data: 25 | return response_data['access_token'], None 26 | else: 27 | return None, "Authentication succeeded but no access token was returned. Please check the response." 28 | else: 29 | # Parsing the error message from the API response 30 | error_detail = response.json() # Assuming the error is in JSON format 31 | error_messages = error_detail.get('errors', []) 32 | detailed_error_message = "; ".join([error['message'] for error in error_messages]) 33 | return None, f"API error: {error_messages}" if detailed_error_message else "Authentication failed. Please try again." 34 | except Exception as e: 35 | return None, f"An exception occurred: {str(e)}" 36 | 37 | -------------------------------------------------------------------------------- /restx_api/place_order.py: -------------------------------------------------------------------------------- 1 | from flask_restx import Namespace, Resource, fields 2 | from flask import request, jsonify, make_response 3 | from limiter import limiter 4 | import os 5 | 6 | from services.place_order_service import place_order 7 | from utils.logging import get_logger 8 | 9 | ORDER_RATE_LIMIT = os.getenv("ORDER_RATE_LIMIT", "10 per second") 10 | api = Namespace('place_order', description='Place Order API') 11 | 12 | # Initialize logger 13 | logger = get_logger(__name__) 14 | 15 | # All functionality moved to place_order_service.py 16 | 17 | @api.route('/', strict_slashes=False) 18 | class PlaceOrder(Resource): 19 | @limiter.limit(ORDER_RATE_LIMIT) 20 | def post(self): 21 | """Place an order with the broker""" 22 | try: 23 | # Get the request data 24 | data = request.json 25 | 26 | # Extract API key without removing it from the original data 27 | api_key = data.get('apikey', None) 28 | 29 | # Call the service function to place the order 30 | success, response_data, status_code = place_order( 31 | order_data=data, 32 | api_key=api_key 33 | ) 34 | 35 | return make_response(jsonify(response_data), status_code) 36 | 37 | except Exception as e: 38 | logger.exception("An unexpected error occurred in PlaceOrder endpoint.") 39 | error_response = { 40 | 'status': 'error', 41 | 'message': 'An unexpected error occurred in the API endpoint' 42 | } 43 | return make_response(jsonify(error_response), 500) 44 | -------------------------------------------------------------------------------- /examples/python/depth_50_example.py: -------------------------------------------------------------------------------- 1 | """ 2 | OpenAlgo WebSocket 50-Level Market Depth Example 3 | For brokers that support deep market depth (Fyers TBT, etc.) 4 | """ 5 | 6 | from openalgo import api 7 | import time 8 | import logging 9 | 10 | # Configure logging to see WebSocket debug output 11 | logging.basicConfig( 12 | level=logging.INFO, 13 | format='%(asctime)s - %(name)s - %(levelname)s - %(message)s' 14 | ) 15 | 16 | # Initialize feed client with explicit parameters 17 | client = api( 18 | api_key="7653f710c940cdf1d757b5a7d808a60f43bc7e9c0239065435861da2869ec0fc", # Replace with your API key 19 | host="http://127.0.0.1:5000", # Replace with your API host 20 | ws_url="ws://127.0.0.1:8765" # Explicit WebSocket URL (can be different from REST API host) 21 | ) 22 | 23 | # Instruments for 50-level depth testing 24 | # Use :50 suffix to request 50-level TBT depth (e.g., "TCS:50") 25 | instruments_list = [ 26 | {"exchange": "NSE", "symbol": "TCS:50"} 27 | ] 28 | 29 | def on_data_received(data): 30 | print("Market Depth Update:") 31 | print(data) 32 | 33 | # Connect and subscribe 34 | client.connect() 35 | client.subscribe_depth(instruments_list, on_data_received=on_data_received) 36 | 37 | # Wait a bit for WebSocket to connect and start receiving data 38 | print("\nWaiting for TBT WebSocket to connect and receive data...") 39 | time.sleep(3) 40 | 41 | # Poll Market Depth data a few times 42 | for i in range(15): 43 | print(f"\nPoll {i+1}:") 44 | depth = client.get_depth() 45 | if depth: 46 | print(depth) 47 | else: 48 | print("No depth data yet...") 49 | time.sleep(1) 50 | 51 | # Cleanup 52 | client.unsubscribe_depth(instruments_list) 53 | client.disconnect() 54 | -------------------------------------------------------------------------------- /restx_api/tradebook.py: -------------------------------------------------------------------------------- 1 | from flask_restx import Namespace, Resource 2 | from flask import request, jsonify, make_response 3 | from marshmallow import ValidationError 4 | from limiter import limiter 5 | import os 6 | from .account_schema import TradebookSchema 7 | from services.tradebook_service import get_tradebook 8 | from utils.logging import get_logger 9 | 10 | API_RATE_LIMIT = os.getenv("API_RATE_LIMIT", "10 per second") 11 | api = Namespace('tradebook', description='Trade Book API') 12 | 13 | # Initialize logger 14 | logger = get_logger(__name__) 15 | 16 | # Initialize schema 17 | tradebook_schema = TradebookSchema() 18 | 19 | @api.route('/', strict_slashes=False) 20 | class Tradebook(Resource): 21 | @limiter.limit(API_RATE_LIMIT) 22 | def post(self): 23 | """Get trade book details""" 24 | try: 25 | # Validate request data 26 | tradebook_data = tradebook_schema.load(request.json) 27 | 28 | api_key = tradebook_data['apikey'] 29 | 30 | # Call the service function to get tradebook data with API key 31 | success, response_data, status_code = get_tradebook(api_key=api_key) 32 | 33 | return make_response(jsonify(response_data), status_code) 34 | 35 | except ValidationError as err: 36 | return make_response(jsonify({ 37 | 'status': 'error', 38 | 'message': err.messages 39 | }), 400) 40 | except Exception as e: 41 | logger.exception(f"Unexpected error in tradebook endpoint: {e}") 42 | return make_response(jsonify({ 43 | 'status': 'error', 44 | 'message': 'An unexpected error occurred' 45 | }), 500) 46 | -------------------------------------------------------------------------------- /restx_api/holdings.py: -------------------------------------------------------------------------------- 1 | from flask_restx import Namespace, Resource 2 | from flask import request, jsonify, make_response 3 | from marshmallow import ValidationError 4 | from limiter import limiter 5 | import os 6 | import traceback 7 | 8 | from .account_schema import HoldingsSchema 9 | from services.holdings_service import get_holdings 10 | from utils.logging import get_logger 11 | 12 | API_RATE_LIMIT = os.getenv("API_RATE_LIMIT", "10 per second") 13 | api = Namespace('holdings', description='Holdings API') 14 | 15 | # Initialize logger 16 | logger = get_logger(__name__) 17 | 18 | # Initialize schema 19 | holdings_schema = HoldingsSchema() 20 | 21 | @api.route('/', strict_slashes=False) 22 | class Holdings(Resource): 23 | @limiter.limit(API_RATE_LIMIT) 24 | def post(self): 25 | """Get holdings details""" 26 | try: 27 | # Validate request data 28 | holdings_data = holdings_schema.load(request.json) 29 | 30 | api_key = holdings_data['apikey'] 31 | 32 | # Call the service function to get holdings data with API key 33 | success, response_data, status_code = get_holdings(api_key=api_key) 34 | 35 | return make_response(jsonify(response_data), status_code) 36 | 37 | except ValidationError as err: 38 | return make_response(jsonify({ 39 | 'status': 'error', 40 | 'message': err.messages 41 | }), 400) 42 | except Exception as e: 43 | logger.exception(f"Unexpected error in holdings endpoint: {e}") 44 | return make_response(jsonify({ 45 | 'status': 'error', 46 | 'message': 'An unexpected error occurred' 47 | }), 500) 48 | -------------------------------------------------------------------------------- /examples/python/depth_20_example.py: -------------------------------------------------------------------------------- 1 | """ 2 | OpenAlgo WebSocket 20-Level Market Depth Example 3 | For brokers that support 20-level depth (Dhan NSE/NFO) 4 | """ 5 | 6 | from openalgo import api 7 | import time 8 | import logging 9 | 10 | # Configure logging to see WebSocket debug output 11 | logging.basicConfig( 12 | level=logging.INFO, 13 | format='%(asctime)s - %(name)s - %(levelname)s - %(message)s' 14 | ) 15 | 16 | # Initialize feed client with explicit parameters 17 | client = api( 18 | api_key="7653f710c940cdf1d757b5a7d808a60f43bc7e9c0239065435861da2869ec0fc", # Replace with your API key 19 | host="http://127.0.0.1:5000", # Replace with your API host 20 | ws_url="ws://127.0.0.1:8765" # Explicit WebSocket URL (can be different from REST API host) 21 | ) 22 | 23 | # Instruments for 20-level depth testing 24 | # Use :20 suffix to request 20-level depth (e.g., "TCS:20") 25 | # NFO also supports 20-level depth 26 | instruments_list = [ 27 | {"exchange": "NSE", "symbol": "TCS:20"}, 28 | ] 29 | 30 | def on_data_received(data): 31 | print("Market Depth Update:") 32 | print(data) 33 | 34 | # Connect and subscribe 35 | client.connect() 36 | client.subscribe_depth(instruments_list, on_data_received=on_data_received) 37 | 38 | # Wait a bit for WebSocket to connect and start receiving data 39 | print("\nWaiting for 20-level depth WebSocket to connect and receive data...") 40 | time.sleep(3) 41 | 42 | # Poll Market Depth data a few times 43 | for i in range(15): 44 | print(f"\nPoll {i+1}:") 45 | depth = client.get_depth() 46 | if depth: 47 | print(depth) 48 | else: 49 | print("No depth data yet...") 50 | time.sleep(1) 51 | 52 | # Cleanup 53 | client.unsubscribe_depth(instruments_list) 54 | client.disconnect() 55 | -------------------------------------------------------------------------------- /restx_api/ping.py: -------------------------------------------------------------------------------- 1 | from flask_restx import Namespace, Resource 2 | from flask import request, jsonify, make_response 3 | from marshmallow import ValidationError 4 | from limiter import limiter 5 | import os 6 | import traceback 7 | 8 | from .account_schema import PingSchema 9 | from services.ping_service import get_ping 10 | from utils.logging import get_logger 11 | 12 | API_RATE_LIMIT = os.getenv("API_RATE_LIMIT", "10 per second") 13 | api = Namespace('ping', description='Ping API to check connectivity and authentication') 14 | 15 | # Initialize logger 16 | logger = get_logger(__name__) 17 | 18 | # Initialize schema 19 | ping_schema = PingSchema() 20 | 21 | @api.route('/', strict_slashes=False) 22 | class Ping(Resource): 23 | @limiter.limit(API_RATE_LIMIT) 24 | def post(self): 25 | """Check API connectivity and authentication""" 26 | try: 27 | # Validate request data 28 | ping_data = ping_schema.load(request.json) 29 | 30 | api_key = ping_data['apikey'] 31 | 32 | # Call the service function to get ping response with API key 33 | success, response_data, status_code = get_ping(api_key=api_key) 34 | return make_response(jsonify(response_data), status_code) 35 | 36 | except ValidationError as err: 37 | return make_response(jsonify({ 38 | 'status': 'error', 39 | 'message': err.messages 40 | }), 400) 41 | except Exception as e: 42 | logger.error(f"Unexpected error in ping endpoint: {e}") 43 | traceback.print_exc() 44 | return make_response(jsonify({ 45 | 'status': 'error', 46 | 'message': 'An unexpected error occurred' 47 | }), 500) -------------------------------------------------------------------------------- /restx_api/orderbook.py: -------------------------------------------------------------------------------- 1 | from flask_restx import Namespace, Resource 2 | from flask import request, jsonify, make_response 3 | from marshmallow import ValidationError 4 | from limiter import limiter 5 | import os 6 | import traceback 7 | 8 | from .account_schema import OrderbookSchema 9 | from services.orderbook_service import get_orderbook 10 | from utils.logging import get_logger 11 | 12 | API_RATE_LIMIT = os.getenv("API_RATE_LIMIT", "10 per second") 13 | api = Namespace('orderbook', description='Order Book API') 14 | 15 | # Initialize logger 16 | logger = get_logger(__name__) 17 | 18 | # Initialize schema 19 | orderbook_schema = OrderbookSchema() 20 | 21 | @api.route('/', strict_slashes=False) 22 | class Orderbook(Resource): 23 | @limiter.limit(API_RATE_LIMIT) 24 | def post(self): 25 | """Get order book details""" 26 | try: 27 | # Validate request data 28 | orderbook_data = orderbook_schema.load(request.json) 29 | 30 | api_key = orderbook_data['apikey'] 31 | 32 | # Call the service function to get orderbook data with API key 33 | success, response_data, status_code = get_orderbook(api_key=api_key) 34 | 35 | return make_response(jsonify(response_data), status_code) 36 | 37 | except ValidationError as err: 38 | return make_response(jsonify({ 39 | 'status': 'error', 40 | 'message': err.messages 41 | }), 400) 42 | except Exception as e: 43 | logger.exception(f"Unexpected error in orderbook endpoint: {e}") 44 | return make_response(jsonify({ 45 | 'status': 'error', 46 | 'message': 'An unexpected error occurred' 47 | }), 500) 48 | -------------------------------------------------------------------------------- /restx_api/intervals.py: -------------------------------------------------------------------------------- 1 | from flask_restx import Namespace, Resource 2 | from flask import request, jsonify, make_response 3 | from marshmallow import ValidationError 4 | from limiter import limiter 5 | import os 6 | import traceback 7 | 8 | from .data_schemas import IntervalsSchema 9 | from services.intervals_service import get_intervals 10 | from utils.logging import get_logger 11 | 12 | API_RATE_LIMIT = os.getenv("API_RATE_LIMIT", "10 per second") 13 | api = Namespace('intervals', description='Supported Intervals API') 14 | 15 | # Initialize logger 16 | logger = get_logger(__name__) 17 | 18 | # Initialize schema 19 | intervals_schema = IntervalsSchema() 20 | 21 | @api.route('/', strict_slashes=False) 22 | class Intervals(Resource): 23 | @limiter.limit(API_RATE_LIMIT) 24 | def post(self): 25 | """Get supported intervals for the broker""" 26 | try: 27 | # Validate request data 28 | intervals_data = intervals_schema.load(request.json) 29 | 30 | api_key = intervals_data['apikey'] 31 | 32 | # Call the service function to get intervals data with API key 33 | success, response_data, status_code = get_intervals(api_key=api_key) 34 | 35 | return make_response(jsonify(response_data), status_code) 36 | 37 | except ValidationError as err: 38 | return make_response(jsonify({ 39 | 'status': 'error', 40 | 'message': err.messages 41 | }), 400) 42 | except Exception as e: 43 | logger.exception(f"Unexpected error in intervals endpoint: {e}") 44 | return make_response(jsonify({ 45 | 'status': 'error', 46 | 'message': 'An unexpected error occurred' 47 | }), 500) 48 | -------------------------------------------------------------------------------- /restx_api/positionbook.py: -------------------------------------------------------------------------------- 1 | from flask_restx import Namespace, Resource 2 | from flask import request, jsonify, make_response 3 | from marshmallow import ValidationError 4 | from limiter import limiter 5 | import os 6 | 7 | from .account_schema import PositionbookSchema 8 | from services.positionbook_service import get_positionbook 9 | from utils.logging import get_logger 10 | 11 | API_RATE_LIMIT = os.getenv("API_RATE_LIMIT", "10 per second") 12 | api = Namespace('positionbook', description='Position Book API') 13 | 14 | # Initialize logger 15 | logger = get_logger(__name__) 16 | 17 | # Initialize schema 18 | positionbook_schema = PositionbookSchema() 19 | 20 | @api.route('/', strict_slashes=False) 21 | class Positionbook(Resource): 22 | @limiter.limit(API_RATE_LIMIT) 23 | def post(self): 24 | """Get position book details""" 25 | try: 26 | # Validate request data 27 | positionbook_data = positionbook_schema.load(request.json) 28 | 29 | api_key = positionbook_data['apikey'] 30 | 31 | # Call the service function to get positionbook data with API key 32 | success, response_data, status_code = get_positionbook(api_key=api_key) 33 | 34 | return make_response(jsonify(response_data), status_code) 35 | 36 | except ValidationError as err: 37 | return make_response(jsonify({ 38 | 'status': 'error', 39 | 'message': err.messages 40 | }), 400) 41 | except Exception as e: 42 | logger.exception(f"Unexpected error in positionbook endpoint: {e}") 43 | return make_response(jsonify({ 44 | 'status': 'error', 45 | 'message': 'An unexpected error occurred' 46 | }), 500) 47 | -------------------------------------------------------------------------------- /restx_api/funds.py: -------------------------------------------------------------------------------- 1 | from flask_restx import Namespace, Resource 2 | from flask import request, jsonify, make_response 3 | from marshmallow import ValidationError 4 | from database.auth_db import get_auth_token_broker 5 | from limiter import limiter 6 | import os 7 | import traceback 8 | 9 | from .account_schema import FundsSchema 10 | from services.funds_service import get_funds 11 | from utils.logging import get_logger 12 | 13 | API_RATE_LIMIT = os.getenv("API_RATE_LIMIT", "10 per second") 14 | api = Namespace('funds', description='Account Funds API') 15 | 16 | # Initialize logger 17 | logger = get_logger(__name__) 18 | 19 | # Initialize schema 20 | funds_schema = FundsSchema() 21 | 22 | @api.route('/', strict_slashes=False) 23 | class Funds(Resource): 24 | @limiter.limit(API_RATE_LIMIT) 25 | def post(self): 26 | """Get account funds and margin details""" 27 | try: 28 | # Validate request data 29 | funds_data = funds_schema.load(request.json) 30 | 31 | api_key = funds_data['apikey'] 32 | 33 | # Call the service function to get funds data with API key 34 | success, response_data, status_code = get_funds(api_key=api_key) 35 | return make_response(jsonify(response_data), status_code) 36 | 37 | except ValidationError as err: 38 | return make_response(jsonify({ 39 | 'status': 'error', 40 | 'message': err.messages 41 | }), 400) 42 | except Exception as e: 43 | logger.error(f"Unexpected error in funds endpoint: {e}") 44 | traceback.print_exc() 45 | return make_response(jsonify({ 46 | 'status': 'error', 47 | 'message': 'An unexpected error occurred' 48 | }), 500) 49 | -------------------------------------------------------------------------------- /templates/index.html: -------------------------------------------------------------------------------- 1 | {% extends "layout.html" %} 2 | 3 | {% block title %}OpenAlgo - OpenSource Algo Platform for Everyone{% endblock %} 4 | 5 | {% block content %} 6 | 7 |
8 |
9 |

10 | Your Personal 11 | Algo Trading 12 | Platform 13 |

14 |

15 | Connect your algo strategies and run from any platform - Amibroker, TradingView, GoCharting, N8N, Python, GO, NodeJs, ChartInk, MetaTrader, Excel, or Google Sheets. And Recieve your Strategy Alerts to Telegram. 16 |

17 | 31 |
32 |
33 | {% endblock %} 34 | -------------------------------------------------------------------------------- /.claude/settings.local.json: -------------------------------------------------------------------------------- 1 | { 2 | "permissions": { 3 | "allow": [ 4 | "Bash(npm run build:css:*)", 5 | "Bash(npm install:*)", 6 | "Bash(npm info:*)", 7 | "mcp__ide__getDiagnostics", 8 | "Bash(npx postcss:*)", 9 | "Bash(npx tailwindcss:*)", 10 | "Bash(node -e \"const config = require(''./tailwind.config.mjs''); console.log(JSON.stringify(config, null, 2))\")", 11 | "Bash(node:*)", 12 | "Bash(cat:*)", 13 | "WebSearch", 14 | "WebFetch(domain:daisyui.com)", 15 | "WebFetch(domain:www.openalgo.in)", 16 | "Bash(curl -L \"https://www.openalgo.in/faq\")", 17 | "Bash(curl -s \"https://www.openalgo.in/_next/data\")", 18 | "WebFetch(domain:github.com)", 19 | "Read(//c/Users/Admin1/Downloads/**)", 20 | "Read(//c/Users/Admin1/Desktop/**)", 21 | "Bash(sqlite3:*)", 22 | "Bash(python:*)", 23 | "Bash(uv run:*)", 24 | "Bash(curl:*)", 25 | "Read(//d/**)", 26 | "Bash(timeout 15 uv run:*)", 27 | "Bash(timeout 10 uv run:*)", 28 | "Bash(dir:*)", 29 | "Bash(move test_option_symbol_api.py test )", 30 | "Bash(move test_options_order_api.py test)", 31 | "Bash(xargs:*)", 32 | "Bash(test:*)", 33 | "Bash(find:*)", 34 | "Bash(copy:*)", 35 | "Bash(del /F \"D:\\openalgo-sandbox-test\\openalgo\\websocket_proxy\\server_optimized.py\")", 36 | "Bash(move \"D:\\openalgo-sandbox-test\\openalgo\\websocket_proxy\\OPTIMIZATION_SUMMARY.md\" \"D:\\openalgo-sandbox-test\\openalgo\\docs\\WEBSOCKET_OPTIMIZATION.md\")", 37 | "WebFetch(domain:docs.openalgo.in)", 38 | "WebFetch(domain:kite.trade)" 39 | "Bash(move docsgocharting_webhook_setup.md staticdocsgocharting_webhook_setup.md)", 40 | "Bash(npx update-browserslist-db@latest:*)" 41 | ], 42 | "deny": [], 43 | "ask": [] 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /docs/docker_env_changes.md: -------------------------------------------------------------------------------- 1 | # Essential .env Changes for Docker Setup (Without Eventlet) 2 | 3 | ## 1. Flask Host Configuration 4 | ```bash 5 | # Change from 127.0.0.1 to 0.0.0.0 to allow external connections 6 | FLASK_HOST_IP='0.0.0.0' # Required for Docker 7 | FLASK_PORT='5000' 8 | ``` 9 | 10 | ## 2. WebSocket Configuration 11 | ```bash 12 | # WebSocket server must bind to 0.0.0.0 inside Docker 13 | WEBSOCKET_HOST='0.0.0.0' # Required for Docker 14 | WEBSOCKET_PORT='8765' 15 | WEBSOCKET_URL='ws://localhost:8765' # URL for clients connecting from host 16 | ``` 17 | 18 | ## 3. ZeroMQ Configuration 19 | ```bash 20 | # ZMQ must also bind to 0.0.0.0 for internal communication 21 | ZMQ_HOST='0.0.0.0' # Required for Docker 22 | ZMQ_PORT='5555' 23 | ``` 24 | 25 | ## Summary of Changes 26 | 27 | ### From (Local Development): 28 | ```bash 29 | FLASK_HOST_IP='127.0.0.1' 30 | WEBSOCKET_HOST='127.0.0.1' 31 | ZMQ_HOST='127.0.0.1' 32 | ``` 33 | 34 | ### To (Docker): 35 | ```bash 36 | FLASK_HOST_IP='0.0.0.0' 37 | WEBSOCKET_HOST='0.0.0.0' 38 | ZMQ_HOST='0.0.0.0' 39 | ``` 40 | 41 | ## Why These Changes? 42 | 43 | 1. **0.0.0.0 vs 127.0.0.1**: 44 | - `127.0.0.1` only allows connections from within the container 45 | - `0.0.0.0` allows connections from outside the container (host machine) 46 | 47 | 2. **WEBSOCKET_URL**: 48 | - Remains as `ws://localhost:8765` because this is the URL clients use from the host machine 49 | - Docker maps the container's port to the host's localhost 50 | 51 | 3. **No other changes needed**: 52 | - All other settings (API keys, database URLs, etc.) remain the same 53 | - The docker-compose.yaml already maps the ports correctly 54 | 55 | ## Verification 56 | 57 | After making these changes and rebuilding Docker: 58 | 59 | 1. Access the web interface: http://localhost:5000 60 | 2. WebSocket connections will work on: ws://localhost:8765 61 | 3. Test with: `python test/simple_ltp_test.py` -------------------------------------------------------------------------------- /restx_api/multiquotes.py: -------------------------------------------------------------------------------- 1 | from flask_restx import Namespace, Resource 2 | from flask import request, jsonify, make_response 3 | from marshmallow import ValidationError 4 | from limiter import limiter 5 | import os 6 | 7 | from .data_schemas import MultiQuotesSchema 8 | from services.quotes_service import get_multiquotes 9 | from utils.logging import get_logger 10 | 11 | API_RATE_LIMIT = os.getenv("API_RATE_LIMIT", "10 per second") 12 | api = Namespace('multiquotes', description='Real-time Multiple Quotes API') 13 | 14 | # Initialize logger 15 | logger = get_logger(__name__) 16 | 17 | # Initialize schema 18 | multiquotes_schema = MultiQuotesSchema() 19 | 20 | @api.route('/', strict_slashes=False) 21 | class MultiQuotes(Resource): 22 | @limiter.limit(API_RATE_LIMIT) 23 | def post(self): 24 | """Get real-time quotes for multiple symbols""" 25 | try: 26 | # Validate request data 27 | multiquotes_data = multiquotes_schema.load(request.json) 28 | 29 | api_key = multiquotes_data['apikey'] 30 | symbols = multiquotes_data['symbols'] 31 | 32 | # Call the service function to get multiquotes data with API key 33 | success, response_data, status_code = get_multiquotes( 34 | symbols=symbols, 35 | api_key=api_key 36 | ) 37 | 38 | return make_response(jsonify(response_data), status_code) 39 | 40 | except ValidationError as err: 41 | return make_response(jsonify({ 42 | 'status': 'error', 43 | 'message': err.messages 44 | }), 400) 45 | except Exception as e: 46 | logger.exception(f"Unexpected error in multiquotes endpoint: {e}") 47 | return make_response(jsonify({ 48 | 'status': 'error', 49 | 'message': 'An unexpected error occurred' 50 | }), 500) 51 | -------------------------------------------------------------------------------- /utils/number_formatter.py: -------------------------------------------------------------------------------- 1 | # utils/number_formatter.py 2 | """ 3 | Number formatting utilities for Indian numbering system 4 | Formats large numbers in Crores (Cr) and Lakhs (L) 5 | """ 6 | 7 | def format_indian_number(value): 8 | """ 9 | Format number in Indian format with Cr/L suffixes 10 | 11 | Examples: 12 | 10000000.0 -> 1.00Cr 13 | 9978000.0 -> 99.78L 14 | 10000.0 -> 10000.00 15 | -5000000.0 -> -50.00L 16 | 17 | Args: 18 | value: Number to format (int, float, or string) 19 | 20 | Returns: 21 | Formatted string with Cr/L suffix or decimal format 22 | """ 23 | try: 24 | # Convert to float 25 | num = float(value) 26 | 27 | # Handle sign 28 | is_negative = num < 0 29 | num = abs(num) 30 | 31 | # Format based on magnitude 32 | if num >= 10000000: # 1 Crore or more 33 | formatted = f"{num / 10000000:.2f}Cr" 34 | elif num >= 100000: # 1 Lakh or more 35 | formatted = f"{num / 100000:.2f}L" 36 | else: 37 | # For numbers less than 1L, show with 2 decimal places 38 | formatted = f"{num:.2f}" 39 | 40 | # Add negative sign if needed 41 | if is_negative: 42 | formatted = f"-{formatted}" 43 | 44 | return formatted 45 | 46 | except (ValueError, TypeError): 47 | # If conversion fails, return original value as string 48 | return str(value) 49 | 50 | 51 | def format_indian_currency(value): 52 | """ 53 | Format number as Indian currency (₹) 54 | 55 | Examples: 56 | 10000000.0 -> ₹1.00Cr 57 | 9978000.0 -> ₹99.78L 58 | 10000.0 -> ₹10000.00 59 | 60 | Args: 61 | value: Number to format 62 | 63 | Returns: 64 | Formatted string with ₹ prefix 65 | """ 66 | formatted = format_indian_number(value) 67 | return f"₹{formatted}" 68 | -------------------------------------------------------------------------------- /restx_api/quotes.py: -------------------------------------------------------------------------------- 1 | from flask_restx import Namespace, Resource 2 | from flask import request, jsonify, make_response 3 | from marshmallow import ValidationError 4 | from limiter import limiter 5 | import os 6 | 7 | from .data_schemas import QuotesSchema 8 | from services.quotes_service import get_quotes 9 | from utils.logging import get_logger 10 | 11 | API_RATE_LIMIT = os.getenv("API_RATE_LIMIT", "10 per second") 12 | api = Namespace('quotes', description='Real-time Quotes API') 13 | 14 | # Initialize logger 15 | logger = get_logger(__name__) 16 | 17 | # Initialize schema 18 | quotes_schema = QuotesSchema() 19 | 20 | @api.route('/', strict_slashes=False) 21 | class Quotes(Resource): 22 | @limiter.limit(API_RATE_LIMIT) 23 | def post(self): 24 | """Get real-time quotes for given symbol""" 25 | try: 26 | # Validate request data 27 | quotes_data = quotes_schema.load(request.json) 28 | 29 | api_key = quotes_data['apikey'] 30 | symbol = quotes_data['symbol'] 31 | exchange = quotes_data['exchange'] 32 | 33 | # Call the service function to get quotes data with API key 34 | success, response_data, status_code = get_quotes( 35 | symbol=symbol, 36 | exchange=exchange, 37 | api_key=api_key 38 | ) 39 | 40 | return make_response(jsonify(response_data), status_code) 41 | 42 | except ValidationError as err: 43 | return make_response(jsonify({ 44 | 'status': 'error', 45 | 'message': err.messages 46 | }), 400) 47 | except Exception as e: 48 | logger.exception(f"Unexpected error in quotes endpoint: {e}") 49 | return make_response(jsonify({ 50 | 'status': 'error', 51 | 'message': 'An unexpected error occurred' 52 | }), 500) 53 | -------------------------------------------------------------------------------- /restx_api/depth.py: -------------------------------------------------------------------------------- 1 | from flask_restx import Namespace, Resource 2 | from flask import request, jsonify, make_response 3 | from marshmallow import ValidationError 4 | from limiter import limiter 5 | import os 6 | import traceback 7 | 8 | from .data_schemas import DepthSchema 9 | from services.depth_service import get_depth 10 | from utils.logging import get_logger 11 | 12 | API_RATE_LIMIT = os.getenv("API_RATE_LIMIT", "10 per second") 13 | api = Namespace('depth', description='Market Depth API') 14 | 15 | # Initialize logger 16 | logger = get_logger(__name__) 17 | 18 | # Initialize schema 19 | depth_schema = DepthSchema() 20 | 21 | @api.route('/', strict_slashes=False) 22 | class Depth(Resource): 23 | @limiter.limit(API_RATE_LIMIT) 24 | def post(self): 25 | """Get market depth for given symbol""" 26 | try: 27 | # Validate request data 28 | depth_data = depth_schema.load(request.json) 29 | 30 | api_key = depth_data['apikey'] 31 | symbol = depth_data['symbol'] 32 | exchange = depth_data['exchange'] 33 | 34 | # Call the service function to get depth data with API key 35 | success, response_data, status_code = get_depth( 36 | symbol=symbol, 37 | exchange=exchange, 38 | api_key=api_key 39 | ) 40 | 41 | return make_response(jsonify(response_data), status_code) 42 | 43 | except ValidationError as err: 44 | return make_response(jsonify({ 45 | 'status': 'error', 46 | 'message': err.messages 47 | }), 400) 48 | except Exception as e: 49 | logger.error(f"Unexpected error in depth endpoint: {e}") 50 | traceback.print_exc() 51 | return make_response(jsonify({ 52 | 'status': 'error', 53 | 'message': 'An unexpected error occurred' 54 | }), 500) 55 | -------------------------------------------------------------------------------- /examples/python/optionchain_example.py: -------------------------------------------------------------------------------- 1 | from openalgo import api 2 | 3 | # Initialize client 4 | client = api( 5 | api_key="83ad96143dd5081d033abcfd20e9108daee5708fbea404121a762bed1e498dd0", 6 | host="http://127.0.0.1:5000" 7 | ) 8 | 9 | # ------------------------------------------------------- 10 | # Get available expiry dates for NIFTY 11 | # ------------------------------------------------------- 12 | expiry_result = client.expiry( 13 | symbol="NIFTY", 14 | exchange="NFO", 15 | instrumenttype="options", 16 | strike_count= 10 17 | ) 18 | 19 | if expiry_result["status"] == "success": 20 | print("Available NIFTY Expiries:") 21 | for exp in expiry_result["data"]: 22 | print(f" {exp}") 23 | else: 24 | print("Failed to fetch expiries :", expiry_result.get("message")) 25 | 26 | # ------------------------------------------------------- 27 | # Get option chain (5 strikes around ATM) 28 | # ------------------------------------------------------- 29 | chain = client.optionchain( 30 | underlying="NIFTY", 31 | exchange="NSE_INDEX", 32 | expiry_date="30DEC25", 33 | strike_count=5 34 | ) 35 | 36 | print("\nNIFTY Option Chain (5 strikes around ATM):") 37 | print("-" * 50) 38 | print(chain) 39 | print("-" * 50) 40 | print("Strike | CE LTP (Label) | PE LTP (Label)") 41 | 42 | if chain["status"] == "success": 43 | print(f"\nUnderlying LTP: {chain['underlying_ltp']}") 44 | print(f"ATM Strike: {chain['atm_strike']}") 45 | 46 | print("\nStrike | CE LTP (Label) | PE LTP (Label)") 47 | print("-" * 50) 48 | 49 | for item in chain["chain"]: 50 | ce = item.get("ce") or {} 51 | pe = item.get("pe") or {} 52 | 53 | print( 54 | f"{item['strike']:>7} | " 55 | f"{ce.get('ltp', '-'):>6} ({ce.get('label', '-'):>4}) | " 56 | f"{pe.get('ltp', '-'):>6} ({pe.get('label', '-'):>4})" 57 | ) 58 | else: 59 | print("Failed to fetch option chain :", chain.get("message")) 60 | -------------------------------------------------------------------------------- /test/test_mstock.py: -------------------------------------------------------------------------------- 1 | import os 2 | import unittest 3 | from openalgo import api as OAClient 4 | from dotenv import load_dotenv 5 | 6 | # Load environment variables from .env file 7 | load_dotenv() 8 | 9 | class TestMstockBroker(unittest.TestCase): 10 | def setUp(self): 11 | """Set up for the test case.""" 12 | # The test assumes that the OpenAlgo server is running and 13 | # the user is already logged into the mstock broker. 14 | self.api_key = os.getenv( 15 | "OPENALGO_API_KEY", 16 | "3bb8d260915ff680a7258108c0483b9eb7675ced31309a36f5846366943ee9fa" 17 | ) 18 | self.client = OAClient(api_key=self.api_key, host="http://127.0.0.1:5000") 19 | 20 | def test_place_order(self): 21 | """Test placing a simple order.""" 22 | # This test requires an active mstock session in the OpenAlgo server 23 | order_response = self.client.placeorder( 24 | strategy="TEST", 25 | symbol="TCS", 26 | exchange="NSE", 27 | price_type="MARKET", 28 | product="MIS", 29 | action="BUY", 30 | quantity=1 31 | ) 32 | self.assertEqual(order_response.get("status"), "success") 33 | self.assertIn("orderid", order_response) 34 | 35 | def test_get_positions(self): 36 | """Test retrieving positions.""" 37 | positions_response = self.client.positionbook() 38 | self.assertEqual(positions_response.get("status"), "success") 39 | 40 | def test_get_holdings(self): 41 | """Test retrieving holdings.""" 42 | holdings_response = self.client.holdings() 43 | self.assertEqual(holdings_response.get("status"), "success") 44 | 45 | def test_get_funds(self): 46 | """Test retrieving funds.""" 47 | funds_response = self.client.funds() 48 | self.assertEqual(funds_response.get("status"), "success") 49 | 50 | if __name__ == '__main__': 51 | unittest.main() 52 | -------------------------------------------------------------------------------- /cors.py: -------------------------------------------------------------------------------- 1 | # cors.py 2 | 3 | from flask_cors import CORS 4 | import os 5 | 6 | def get_cors_config(): 7 | """ 8 | Get CORS configuration from environment variables. 9 | Returns a dictionary with CORS configuration options. 10 | """ 11 | cors_config = {} 12 | 13 | # Check if CORS is enabled 14 | cors_enabled = os.getenv('CORS_ENABLED', 'FALSE').upper() == 'TRUE' 15 | 16 | if not cors_enabled: 17 | # If CORS is disabled, return empty config (will use Flask-CORS defaults) 18 | return cors_config 19 | 20 | # Get allowed origins 21 | allowed_origins = os.getenv('CORS_ALLOWED_ORIGINS') 22 | if allowed_origins: 23 | cors_config['origins'] = [origin.strip() for origin in allowed_origins.split(',')] 24 | 25 | # Get allowed methods 26 | allowed_methods = os.getenv('CORS_ALLOWED_METHODS') 27 | if allowed_methods: 28 | cors_config['methods'] = [method.strip() for method in allowed_methods.split(',')] 29 | 30 | # Get allowed headers 31 | allowed_headers = os.getenv('CORS_ALLOWED_HEADERS') 32 | if allowed_headers: 33 | cors_config['allow_headers'] = [header.strip() for header in allowed_headers.split(',')] 34 | 35 | # Get exposed headers 36 | exposed_headers = os.getenv('CORS_EXPOSED_HEADERS') 37 | if exposed_headers: 38 | cors_config['expose_headers'] = [header.strip() for header in exposed_headers.split(',')] 39 | 40 | # Check if credentials are allowed 41 | credentials = os.getenv('CORS_ALLOW_CREDENTIALS', 'FALSE').upper() == 'TRUE' 42 | if credentials: 43 | cors_config['supports_credentials'] = True 44 | 45 | # Max age for preflight requests 46 | max_age = os.getenv('CORS_MAX_AGE') 47 | if max_age and max_age.isdigit(): 48 | cors_config['max_age'] = int(max_age) 49 | 50 | return cors_config 51 | 52 | # Initialize Flask-CORS without the app object 53 | cors = CORS(resources={r"/api/*": get_cors_config()}) -------------------------------------------------------------------------------- /restx_api/search.py: -------------------------------------------------------------------------------- 1 | from flask_restx import Namespace, Resource 2 | from flask import request, jsonify, make_response 3 | from marshmallow import ValidationError 4 | from limiter import limiter 5 | import os 6 | 7 | from .data_schemas import SearchSchema 8 | from services.search_service import search_symbols 9 | from utils.logging import get_logger 10 | 11 | API_RATE_LIMIT = os.getenv("API_RATE_LIMIT", "10 per second") 12 | api = Namespace('search', description='Symbol search API') 13 | 14 | # Initialize logger 15 | logger = get_logger(__name__) 16 | 17 | # Initialize schema 18 | search_schema = SearchSchema() 19 | 20 | @api.route('/', strict_slashes=False) 21 | class Search(Resource): 22 | @limiter.limit(API_RATE_LIMIT) 23 | def post(self): 24 | """Search for symbols in the database""" 25 | try: 26 | # Validate request data 27 | search_data = search_schema.load(request.json) 28 | 29 | # Extract parameters 30 | api_key = search_data.pop('apikey', None) 31 | query = search_data['query'] 32 | exchange = search_data.get('exchange') 33 | 34 | # Call the service function to search symbols 35 | success, response_data, status_code = search_symbols( 36 | query=query, 37 | exchange=exchange, 38 | api_key=api_key 39 | ) 40 | 41 | return make_response(jsonify(response_data), status_code) 42 | 43 | except ValidationError as err: 44 | return make_response(jsonify({ 45 | 'status': 'error', 46 | 'message': err.messages 47 | }), 400) 48 | 49 | except Exception as e: 50 | logger.exception(f"Unexpected error in search endpoint: {e}") 51 | return make_response(jsonify({ 52 | 'status': 'error', 53 | 'message': 'An unexpected error occurred' 54 | }), 500) -------------------------------------------------------------------------------- /utils/socketio_error_handler.py: -------------------------------------------------------------------------------- 1 | """ 2 | Socket.IO Error Handler 3 | Handles common Socket.IO errors like disconnected sessions gracefully 4 | """ 5 | 6 | from utils.logging import get_logger 7 | from flask_socketio import disconnect 8 | import functools 9 | 10 | logger = get_logger(__name__) 11 | 12 | def handle_disconnected_session(f): 13 | """ 14 | Decorator to handle disconnected session errors in Socket.IO event handlers 15 | """ 16 | @functools.wraps(f) 17 | def wrapper(*args, **kwargs): 18 | try: 19 | return f(*args, **kwargs) 20 | except KeyError as e: 21 | if str(e) == "'Session is disconnected'": 22 | logger.debug(f"Socket.IO session already disconnected in {f.__name__}") 23 | disconnect() 24 | return None 25 | raise 26 | except Exception as e: 27 | if "Session is disconnected" in str(e): 28 | logger.debug(f"Socket.IO session disconnected in {f.__name__}: {e}") 29 | disconnect() 30 | return None 31 | raise 32 | return wrapper 33 | 34 | def init_socketio_error_handling(socketio_instance): 35 | """ 36 | Initialize Socket.IO error handling 37 | 38 | Args: 39 | socketio_instance: The Flask-SocketIO instance 40 | """ 41 | 42 | @socketio_instance.on_error_default 43 | def default_error_handler(e): 44 | """ 45 | Default error handler for all namespaces 46 | """ 47 | error_msg = str(e) 48 | 49 | # Handle common disconnection errors silently 50 | if "Session is disconnected" in error_msg: 51 | logger.debug(f"Socket.IO session disconnected: {error_msg}") 52 | return False # Don't emit error to client 53 | 54 | # Log other errors 55 | logger.error(f"Socket.IO error: {e}") 56 | return True # Let the error propagate 57 | 58 | logger.debug("Socket.IO error handling initialized") -------------------------------------------------------------------------------- /broker/angel/api/funds.py: -------------------------------------------------------------------------------- 1 | # api/funds.py 2 | 3 | import os 4 | import httpx 5 | import json 6 | from utils.httpx_client import get_httpx_client 7 | from utils.logging import get_logger 8 | 9 | logger = get_logger(__name__) 10 | 11 | 12 | def get_margin_data(auth_token): 13 | """Fetch margin data from the broker's API using the provided auth token.""" 14 | api_key = os.getenv('BROKER_API_KEY') 15 | 16 | # Get the shared httpx client with connection pooling 17 | client = get_httpx_client() 18 | 19 | headers = { 20 | 'Authorization': f'Bearer {auth_token}', 21 | 'Content-Type': 'application/json', 22 | 'Accept': 'application/json', 23 | 'X-UserType': 'USER', 24 | 'X-SourceID': 'WEB', 25 | 'X-ClientLocalIP': 'CLIENT_LOCAL_IP', 26 | 'X-ClientPublicIP': 'CLIENT_PUBLIC_IP', 27 | 'X-MACAddress': 'MAC_ADDRESS', 28 | 'X-PrivateKey': api_key 29 | } 30 | 31 | response = client.get( 32 | "https://apiconnect.angelbroking.com/rest/secure/angelbroking/user/v1/getRMS", 33 | headers=headers 34 | ) 35 | 36 | # Add status attribute for compatibility with the existing codebase 37 | response.status = response.status_code 38 | 39 | margin_data = json.loads(response.text) 40 | 41 | logger.info(f"Margin Data: {margin_data}") 42 | 43 | if margin_data.get('data'): 44 | required_keys = [ 45 | "availablecash", 46 | "collateral", 47 | "m2mrealized", 48 | "m2munrealized", 49 | "utiliseddebits" 50 | ] 51 | filtered_data = {} 52 | for key in required_keys: 53 | value = margin_data['data'].get(key, 0) 54 | try: 55 | formatted_value = "{:.2f}".format(float(value)) 56 | except (ValueError, TypeError): 57 | formatted_value = value 58 | filtered_data[key] = formatted_value 59 | return filtered_data 60 | else: 61 | return {} 62 | -------------------------------------------------------------------------------- /broker/angel/api/auth_api.py: -------------------------------------------------------------------------------- 1 | import httpx 2 | import json 3 | import os 4 | from utils.httpx_client import get_httpx_client 5 | 6 | def authenticate_broker(clientcode, broker_pin, totp_code): 7 | """ 8 | Authenticate with the broker and return the auth token. 9 | """ 10 | api_key = os.getenv('BROKER_API_KEY') 11 | 12 | try: 13 | # Get the shared httpx client 14 | client = get_httpx_client() 15 | 16 | payload = json.dumps({ 17 | "clientcode": clientcode, 18 | "password": broker_pin, 19 | "totp": totp_code 20 | }) 21 | headers = { 22 | 'Content-Type': 'application/json', 23 | 'Accept': 'application/json', 24 | 'X-UserType': 'USER', 25 | 'X-SourceID': 'WEB', 26 | 'X-ClientLocalIP': 'CLIENT_LOCAL_IP', # Ensure these are handled or replaced appropriately 27 | 'X-ClientPublicIP': 'CLIENT_PUBLIC_IP', 28 | 'X-MACAddress': 'MAC_ADDRESS', 29 | 'X-PrivateKey': api_key 30 | } 31 | 32 | response = client.post( 33 | "https://apiconnect.angelbroking.com/rest/auth/angelbroking/user/v1/loginByPassword", 34 | headers=headers, 35 | content=payload 36 | ) 37 | 38 | # Add status attribute for compatibility with the existing codebase 39 | response.status = response.status_code 40 | 41 | data = response.text 42 | data_dict = json.loads(data) 43 | 44 | if 'data' in data_dict and 'jwtToken' in data_dict['data']: 45 | # Return both JWT token and feed token if available (None if not) 46 | auth_token = data_dict['data']['jwtToken'] 47 | feed_token = data_dict['data'].get('feedToken', None) 48 | return auth_token, feed_token, None 49 | else: 50 | return None, None, data_dict.get('message', 'Authentication failed. Please try again.') 51 | except Exception as e: 52 | return None, None, str(e) 53 | -------------------------------------------------------------------------------- /restx_api/symbol.py: -------------------------------------------------------------------------------- 1 | from flask_restx import Namespace, Resource 2 | from flask import request, jsonify, make_response 3 | from marshmallow import ValidationError 4 | from limiter import limiter 5 | import os 6 | 7 | from .data_schemas import SymbolSchema 8 | from services.symbol_service import get_symbol_info 9 | from utils.logging import get_logger 10 | 11 | API_RATE_LIMIT = os.getenv("API_RATE_LIMIT", "10 per second") 12 | api = Namespace('symbol', description='Symbol information API') 13 | 14 | # Initialize logger 15 | logger = get_logger(__name__) 16 | 17 | # Initialize schema 18 | symbol_schema = SymbolSchema() 19 | 20 | @api.route('/', strict_slashes=False) 21 | class Symbol(Resource): 22 | @limiter.limit(API_RATE_LIMIT) 23 | def post(self): 24 | """Get symbol information for a given symbol and exchange""" 25 | try: 26 | # Validate request data 27 | symbol_data = symbol_schema.load(request.json) 28 | 29 | # Extract parameters 30 | api_key = symbol_data.pop('apikey', None) 31 | symbol = symbol_data['symbol'] 32 | exchange = symbol_data['exchange'] 33 | 34 | # Call the service function to get symbol information 35 | success, response_data, status_code = get_symbol_info( 36 | symbol=symbol, 37 | exchange=exchange, 38 | api_key=api_key 39 | ) 40 | 41 | return make_response(jsonify(response_data), status_code) 42 | 43 | except ValidationError as err: 44 | return make_response(jsonify({ 45 | 'status': 'error', 46 | 'message': err.messages 47 | }), 400) 48 | 49 | except Exception as e: 50 | logger.exception(f"Unexpected error in symbol endpoint: {e}") 51 | return make_response(jsonify({ 52 | 'status': 'error', 53 | 'message': 'An unexpected error occurred' 54 | }), 500) 55 | -------------------------------------------------------------------------------- /broker/firstock/mapping/margin_data.py: -------------------------------------------------------------------------------- 1 | # Mapping OpenAlgo API Request https://openalgo.in/docs 2 | # Firstock does not provide position-specific Margin Calculator API 3 | 4 | from utils.logging import get_logger 5 | 6 | logger = get_logger(__name__) 7 | 8 | def transform_margin_position(position, user_id): 9 | """ 10 | Transform a single OpenAlgo margin position to broker format. 11 | 12 | Note: Firstock does not provide a position-specific margin calculator API. 13 | The available Limit API only returns account-level margin information. 14 | 15 | Args: 16 | position: Position in OpenAlgo format 17 | user_id: Firstock user ID 18 | 19 | Raises: 20 | NotImplementedError: Firstock does not support position-specific margin calculator API 21 | """ 22 | raise NotImplementedError("Firstock does not support position-specific margin calculator API") 23 | 24 | def parse_margin_response(response_data): 25 | """ 26 | Parse broker margin calculator response to OpenAlgo standard format. 27 | 28 | Note: Firstock does not provide a position-specific margin calculator API. 29 | The available Limit API only returns account-level margin information. 30 | 31 | Args: 32 | response_data: Raw response from broker margin calculator API 33 | 34 | Raises: 35 | NotImplementedError: Firstock does not support position-specific margin calculator API 36 | """ 37 | raise NotImplementedError("Firstock does not support position-specific margin calculator API") 38 | 39 | def parse_batch_margin_response(responses): 40 | """ 41 | Parse multiple margin responses and aggregate them. 42 | 43 | Note: Firstock does not provide a position-specific margin calculator API. 44 | 45 | Args: 46 | responses: List of individual margin responses 47 | 48 | Raises: 49 | NotImplementedError: Firstock does not support position-specific margin calculator API 50 | """ 51 | raise NotImplementedError("Firstock does not support position-specific margin calculator API") 52 | -------------------------------------------------------------------------------- /test/test_telegram_config.py: -------------------------------------------------------------------------------- 1 | """ 2 | Test script to verify Telegram bot configuration saving and loading 3 | """ 4 | import sys 5 | import os 6 | # Add parent directory to path for imports 7 | sys.path.insert(0, os.path.dirname(os.path.dirname(os.path.abspath(__file__)))) 8 | 9 | from database.telegram_db import get_bot_config, update_bot_config 10 | 11 | def test_config(): 12 | """Test configuration save and load""" 13 | print("Testing Telegram Bot Configuration") 14 | print("=" * 50) 15 | 16 | # Get current config 17 | print("\n1. Current Configuration:") 18 | config = get_bot_config() 19 | for key, value in config.items(): 20 | if key in ['bot_token', 'token']: 21 | if value: 22 | print(f" {key}: {value[:10]}..." if value else f" {key}: None") 23 | else: 24 | print(f" {key}: None") 25 | else: 26 | print(f" {key}: {value}") 27 | 28 | # Test saving configuration 29 | print("\n2. Testing Save Configuration:") 30 | test_config = { 31 | 'bot_token': 'test_token_123456789', 32 | 'broadcast_enabled': True, 33 | 'rate_limit_per_minute': 60 34 | } 35 | 36 | success = update_bot_config(test_config) 37 | print(f" Save result: {'Success' if success else 'Failed'}") 38 | 39 | # Verify saved configuration 40 | print("\n3. Configuration After Save:") 41 | config = get_bot_config() 42 | for key, value in config.items(): 43 | if key in ['bot_token', 'token']: 44 | if value: 45 | print(f" {key}: {value[:10]}..." if value else f" {key}: None") 46 | else: 47 | print(f" {key}: None") 48 | else: 49 | print(f" {key}: {value}") 50 | 51 | # Check specific fields 52 | print("\n4. Verification:") 53 | print(f" Token saved correctly: {config.get('bot_token', '').startswith('test_token')}") 54 | print(f" Broadcast enabled: {config.get('broadcast_enabled')}") 55 | print(f" Rate limit: {config.get('rate_limit_per_minute')}") 56 | 57 | if __name__ == "__main__": 58 | test_config() -------------------------------------------------------------------------------- /restx_api/expiry.py: -------------------------------------------------------------------------------- 1 | from flask_restx import Namespace, Resource 2 | from flask import request, jsonify, make_response 3 | from marshmallow import ValidationError 4 | from limiter import limiter 5 | import os 6 | 7 | from .data_schemas import ExpirySchema 8 | from services.expiry_service import get_expiry_dates 9 | from utils.logging import get_logger 10 | 11 | API_RATE_LIMIT = os.getenv("API_RATE_LIMIT", "10 per second") 12 | api = Namespace('expiry', description='Expiry dates API for F&O instruments') 13 | 14 | # Initialize logger 15 | logger = get_logger(__name__) 16 | 17 | # Initialize schema 18 | expiry_schema = ExpirySchema() 19 | 20 | @api.route('/', strict_slashes=False) 21 | class Expiry(Resource): 22 | @limiter.limit(API_RATE_LIMIT) 23 | def post(self): 24 | """Get expiry dates for F&O symbols (futures or options) for a given underlying symbol""" 25 | try: 26 | # Validate request data 27 | expiry_data = expiry_schema.load(request.json) 28 | 29 | # Extract parameters 30 | api_key = expiry_data.pop('apikey', None) 31 | symbol = expiry_data['symbol'] 32 | exchange = expiry_data['exchange'] 33 | instrumenttype = expiry_data['instrumenttype'] 34 | 35 | # Call the service function to get expiry dates 36 | success, response_data, status_code = get_expiry_dates( 37 | symbol=symbol, 38 | exchange=exchange, 39 | instrumenttype=instrumenttype, 40 | api_key=api_key 41 | ) 42 | 43 | return make_response(jsonify(response_data), status_code) 44 | 45 | except ValidationError as err: 46 | return make_response(jsonify({ 47 | 'status': 'error', 48 | 'message': err.messages 49 | }), 400) 50 | 51 | except Exception as e: 52 | logger.exception(f"Unexpected error in expiry endpoint: {e}") 53 | return make_response(jsonify({ 54 | 'status': 'error', 55 | 'message': 'An unexpected error occurred' 56 | }), 500) -------------------------------------------------------------------------------- /database/token_db.py: -------------------------------------------------------------------------------- 1 | """ 2 | Token Database Module - Enhanced with Full Memory Cache 3 | This module provides the same API as before but now uses intelligent in-memory caching 4 | for 100,000+ symbols with O(1) lookup performance. 5 | 6 | All existing code will continue to work without any changes. 7 | """ 8 | 9 | # Import all functions from the enhanced module 10 | # This makes the enhanced cache transparent to existing code 11 | from database.token_db_enhanced import ( 12 | get_token, 13 | get_symbol, 14 | get_oa_symbol, 15 | get_br_symbol, 16 | get_brexchange, 17 | get_symbol_info, 18 | get_symbol_count, 19 | # Additional functions for backward compatibility 20 | get_token_dbquery, 21 | get_symbol_dbquery, 22 | get_oa_symbol_dbquery, 23 | get_br_symbol_dbquery, 24 | get_brexchange_dbquery, 25 | get_symbol_info_dbquery, 26 | # Data types 27 | SymbolData, 28 | # New bulk operations (optional - won't break existing code) 29 | get_tokens_bulk, 30 | get_symbols_bulk, 31 | search_symbols, 32 | # Cache management (optional - won't break existing code) 33 | load_cache_for_broker, 34 | clear_cache, 35 | get_cache_stats 36 | ) 37 | 38 | # For complete backward compatibility, also expose the old cache variable 39 | # (though it's not used anymore, some code might reference it) 40 | from cachetools import TTLCache 41 | token_cache = TTLCache(maxsize=1024, ttl=3600) # Dummy cache for compatibility 42 | 43 | # Re-export everything so imports work identically 44 | __all__ = [ 45 | 'get_token', 46 | 'get_symbol', 47 | 'get_oa_symbol', 48 | 'get_br_symbol', 49 | 'get_brexchange', 50 | 'get_symbol_info', 51 | 'get_symbol_count', 52 | 'get_token_dbquery', 53 | 'get_symbol_dbquery', 54 | 'get_oa_symbol_dbquery', 55 | 'get_br_symbol_dbquery', 56 | 'get_brexchange_dbquery', 57 | 'get_symbol_info_dbquery', 58 | 'token_cache', # For backward compatibility 59 | # Data types 60 | 'SymbolData', 61 | # New functions (won't affect existing code) 62 | 'get_tokens_bulk', 63 | 'get_symbols_bulk', 64 | 'search_symbols', 65 | 'load_cache_for_broker', 66 | 'clear_cache', 67 | 'get_cache_stats' 68 | ] -------------------------------------------------------------------------------- /restx_api/history.py: -------------------------------------------------------------------------------- 1 | from flask_restx import Namespace, Resource 2 | from flask import request, jsonify, make_response 3 | from marshmallow import ValidationError 4 | from limiter import limiter 5 | import os 6 | import traceback 7 | 8 | from .data_schemas import HistorySchema 9 | from services.history_service import get_history 10 | from utils.logging import get_logger 11 | 12 | API_RATE_LIMIT = os.getenv("API_RATE_LIMIT", "10 per second") 13 | api = Namespace('history', description='Historical Data API') 14 | 15 | # Initialize logger 16 | logger = get_logger(__name__) 17 | 18 | # Initialize schema 19 | history_schema = HistorySchema() 20 | 21 | @api.route('/', strict_slashes=False) 22 | class History(Resource): 23 | @limiter.limit(API_RATE_LIMIT) 24 | def post(self): 25 | """Get historical data for given symbol""" 26 | try: 27 | # Validate request data 28 | history_data = history_schema.load(request.json) 29 | 30 | api_key = history_data['apikey'] 31 | symbol = history_data['symbol'] 32 | exchange = history_data['exchange'] 33 | interval = history_data['interval'] 34 | start_date = history_data['start_date'] 35 | end_date = history_data['end_date'] 36 | 37 | # Call the service function to get historical data with API key 38 | success, response_data, status_code = get_history( 39 | symbol=symbol, 40 | exchange=exchange, 41 | interval=interval, 42 | start_date=start_date, 43 | end_date=end_date, 44 | api_key=api_key 45 | ) 46 | 47 | return make_response(jsonify(response_data), status_code) 48 | 49 | except ValidationError as err: 50 | return make_response(jsonify({ 51 | 'status': 'error', 52 | 'message': err.messages 53 | }), 400) 54 | except Exception as e: 55 | logger.exception(f"Unexpected error in history endpoint: {e}") 56 | return make_response(jsonify({ 57 | 'status': 'error', 58 | 'message': 'An unexpected error occurred' 59 | }), 500) 60 | -------------------------------------------------------------------------------- /download/README.md: -------------------------------------------------------------------------------- 1 | # IEOD Data Downloader 2 | 3 | This tool allows you to download Intraday End of Day (IEOD) data for specified stock symbols from the OpenAlgo API. 4 | 5 | ## Prerequisites 6 | 7 | - Python 3.x 8 | - Valid OpenAlgo API key 9 | - Access to OpenAlgo API endpoint (ensure openalgo is running) 10 | 11 | ## File Structure 12 | 13 | - `ieod.py`: Main script for downloading IEOD data 14 | - `symbols.csv`: List of stock symbols to download data for 15 | - `checkpoint.txt`: Tracks download progress (automatically created) 16 | - `data_download.log`: Log file for download operations (automatically created) 17 | 18 | ## Setup 19 | 20 | 1. Ensure you have a valid API key from OpenAlgo 21 | 2. Place your stock symbols in `symbols.csv` (one symbol per line) 22 | 3. The script will automatically create necessary folders and files 23 | 24 | ## Usage 25 | 26 | Run the script using Python: 27 | 28 | ```bash 29 | python ieod.py 30 | ``` 31 | 32 | ### Download Options 33 | 34 | The script provides two modes of operation: 35 | 36 | 1. Fresh Download 37 | 2. Continue from Last Checkpoint 38 | 39 | ### Time Period Options 40 | 41 | You can select from various time periods for data download: 42 | 43 | 1. Today's Data 44 | 2. Last 5 Days Data 45 | 3. Last 30 Days Data 46 | 4. Last 90 Days Data 47 | 5. Last 1 Year Data 48 | 6. Last 2 Years Data 49 | 7. Last 5 Years Data 50 | 8. Last 10 Years Data 51 | 52 | ### Output 53 | 54 | - Downloaded data is saved in the `symbols` folder 55 | - Each symbol's data is saved in a separate CSV file 56 | - Progress is tracked in `checkpoint.txt` 57 | - Download logs are saved in `data_download.log` 58 | 59 | ## symbols.csv Format 60 | 61 | The `symbols.csv` file should contain one stock symbol per line. Example: 62 | 63 | ``` 64 | RELIANCE 65 | ICICIBANK 66 | HDFCBANK 67 | SBIN 68 | TCS 69 | INFY 70 | ``` 71 | 72 | ## Error Handling 73 | 74 | - The script includes error handling and logging 75 | - Failed downloads are logged in `data_download.log` 76 | - The checkpoint system allows resuming interrupted downloads 77 | 78 | ## Notes 79 | 80 | - Data is downloaded in batches to manage memory efficiently 81 | - Default batch size is 10 symbols (adjustable in the code) 82 | - The script includes rate limiting to prevent API overload 83 | -------------------------------------------------------------------------------- /blueprints/settings.py: -------------------------------------------------------------------------------- 1 | # blueprints/settings.py 2 | 3 | from flask import Blueprint, jsonify, request 4 | from database.settings_db import get_analyze_mode, set_analyze_mode 5 | from utils.session import check_session_validity 6 | from utils.logging import get_logger 7 | from sandbox.execution_thread import start_execution_engine, stop_execution_engine 8 | 9 | logger = get_logger(__name__) 10 | 11 | settings_bp = Blueprint('settings_bp', __name__, url_prefix='/settings') 12 | 13 | @settings_bp.route('/analyze-mode') 14 | @check_session_validity 15 | def get_mode(): 16 | """Get current analyze mode setting""" 17 | try: 18 | return jsonify({'analyze_mode': get_analyze_mode()}) 19 | except Exception as e: 20 | logger.error(f"Error getting analyze mode: {str(e)}") 21 | return jsonify({'error': 'Failed to get analyze mode'}), 500 22 | 23 | @settings_bp.route('/analyze-mode/', methods=['POST']) 24 | @check_session_validity 25 | def set_mode(mode): 26 | """Set analyze mode setting and manage execution engine thread""" 27 | try: 28 | set_analyze_mode(bool(mode)) 29 | mode_name = 'Analyze' if mode else 'Live' 30 | 31 | # Start or stop execution engine based on mode 32 | if mode: 33 | # Starting Analyze mode - start execution engine 34 | success, message = start_execution_engine() 35 | if success: 36 | logger.info("Execution engine started for Analyze mode") 37 | else: 38 | logger.warning(f"Failed to start execution engine: {message}") 39 | else: 40 | # Switching to Live mode - stop execution engine 41 | success, message = stop_execution_engine() 42 | if success: 43 | logger.info("Execution engine stopped for Live mode") 44 | else: 45 | logger.warning(f"Failed to stop execution engine: {message}") 46 | 47 | return jsonify({ 48 | 'success': True, 49 | 'analyze_mode': bool(mode), 50 | 'message': f'Switched to {mode_name} Mode' 51 | }) 52 | except Exception as e: 53 | logger.error(f"Error setting analyze mode: {str(e)}") 54 | return jsonify({'error': 'Failed to set analyze mode'}), 500 55 | -------------------------------------------------------------------------------- /test/test_telegram_bot.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | """ 3 | Test script to verify Telegram bot functionality 4 | """ 5 | import asyncio 6 | import logging 7 | import sys 8 | import os 9 | # Add parent directory to path for imports 10 | sys.path.insert(0, os.path.dirname(os.path.dirname(os.path.abspath(__file__)))) 11 | 12 | from database.telegram_db import get_bot_config 13 | from services.telegram_bot_service import TelegramBotService 14 | 15 | # Set up logging 16 | logging.basicConfig( 17 | level=logging.INFO, 18 | format='%(asctime)s - %(name)s - %(levelname)s - %(message)s' 19 | ) 20 | 21 | async def test_bot(): 22 | """Test the bot initialization and start""" 23 | bot_service = TelegramBotService() 24 | 25 | # Get bot config from database 26 | config = get_bot_config() 27 | 28 | if not config.get('token'): 29 | print("[ERROR] No bot token configured. Please configure the bot token first.") 30 | return 31 | 32 | print(f"[OK] Bot token found: {config['token'][:10] if config.get('token') else 'None'}...") 33 | 34 | # Initialize bot 35 | success, message = await bot_service.initialize_bot(config['token']) 36 | if success: 37 | print(f"[OK] Bot initialized: {message}") 38 | 39 | # Try to get bot info 40 | if bot_service.bot: 41 | bot_info = await bot_service.bot.get_me() 42 | print(f"[OK] Bot username: @{bot_info.username}") 43 | print(f"[OK] Bot name: {bot_info.first_name}") 44 | else: 45 | print(f"[ERROR] Failed to initialize bot: {message}") 46 | return 47 | 48 | # Start polling 49 | print("Starting bot polling...") 50 | success, message = await bot_service.start_polling() 51 | if success: 52 | print(f"[OK] {message}") 53 | print("Bot is now running. Press Ctrl+C to stop.") 54 | 55 | # Keep the bot running 56 | try: 57 | while bot_service.is_running: 58 | await asyncio.sleep(1) 59 | except KeyboardInterrupt: 60 | print("\nStopping bot...") 61 | await bot_service.stop_bot() 62 | print("Bot stopped.") 63 | else: 64 | print(f"[ERROR] Failed to start polling: {message}") 65 | 66 | if __name__ == "__main__": 67 | asyncio.run(test_bot()) -------------------------------------------------------------------------------- /utils/security_middleware.py: -------------------------------------------------------------------------------- 1 | from flask import request, abort, jsonify 2 | from database.traffic_db import IPBan, Error404Tracker, logs_session 3 | from functools import wraps 4 | from utils.ip_helper import get_real_ip, get_real_ip_from_environ 5 | import logging 6 | 7 | logger = logging.getLogger(__name__) 8 | 9 | class SecurityMiddleware: 10 | """Middleware to check for banned IPs and handle security""" 11 | 12 | def __init__(self, app): 13 | self.app = app 14 | 15 | def __call__(self, environ, start_response): 16 | # Get real client IP (handles proxies) 17 | client_ip = get_real_ip_from_environ(environ) 18 | 19 | # Check if IP is banned 20 | if IPBan.is_ip_banned(client_ip): 21 | # Return 403 Forbidden for banned IPs 22 | status = '403 Forbidden' 23 | headers = [('Content-Type', 'text/plain')] 24 | start_response(status, headers) 25 | logger.warning(f"Blocked banned IP: {client_ip}") 26 | return [b'Access Denied: Your IP has been banned'] 27 | 28 | # Continue with normal request processing 29 | return self.app(environ, start_response) 30 | 31 | def check_ip_ban(f): 32 | """Decorator to check if IP is banned before processing request""" 33 | @wraps(f) 34 | def decorated_function(*args, **kwargs): 35 | client_ip = get_real_ip() 36 | 37 | if IPBan.is_ip_banned(client_ip): 38 | logger.warning(f"Blocked banned IP in decorator: {client_ip}") 39 | abort(403, description="Access Denied: Your IP has been banned") 40 | 41 | return f(*args, **kwargs) 42 | 43 | return decorated_function 44 | 45 | def init_security_middleware(app): 46 | """Initialize security middleware""" 47 | # Wrap the WSGI app with security middleware 48 | app.wsgi_app = SecurityMiddleware(app.wsgi_app) 49 | 50 | logger.debug("Security middleware initialized") 51 | 52 | # Note: 404 handler is now in app.py to avoid conflicts 53 | # The main app's 404 handler calls Error404Tracker.track_404() 54 | 55 | # Register 403 error handler for banned IPs 56 | @app.errorhandler(403) 57 | def handle_403(e): 58 | return jsonify({'error': 'Access Denied'}), 403 59 | 60 | logger.debug("Security middleware initialized") -------------------------------------------------------------------------------- /test/sandbox/test_rejected_order.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | """ 3 | Test that rejected CNC SELL orders appear in the orderbook 4 | """ 5 | from decimal import Decimal 6 | from database.sandbox_db import init_db, db_session, SandboxOrders, SandboxPositions, SandboxFunds 7 | from sandbox.order_manager import OrderManager 8 | 9 | # Initialize database 10 | init_db() 11 | 12 | # Test user 13 | user_id = 'rajandran' 14 | 15 | # Clear ALL previous orders to avoid ID conflicts 16 | SandboxOrders.query.delete() # Delete ALL orders 17 | SandboxPositions.query.filter_by(user_id=user_id, symbol='ZEEL', product='CNC').delete() 18 | db_session.commit() 19 | 20 | # Ensure user has funds 21 | funds = SandboxFunds.query.filter_by(user_id=user_id).first() 22 | if not funds: 23 | funds = SandboxFunds( 24 | user_id=user_id, 25 | total_capital=Decimal('10000000.00'), 26 | available_balance=Decimal('10000000.00'), 27 | used_margin=Decimal('0.00') 28 | ) 29 | db_session.add(funds) 30 | db_session.commit() 31 | 32 | print("🧪 Testing Rejected Order in Orderbook") 33 | print("="*60) 34 | 35 | # Create order manager 36 | om = OrderManager(user_id) 37 | 38 | # Try to place a CNC SELL order without position 39 | print("\n→ Attempting CNC SELL 100 ZEEL (no position)...") 40 | success, response, code = om.place_order({ 41 | 'symbol': 'ZEEL', 42 | 'exchange': 'NSE', 43 | 'action': 'SELL', 44 | 'quantity': 100, 45 | 'price_type': 'MARKET', 46 | 'product': 'CNC' 47 | }) 48 | 49 | print(f"Response: {response}") 50 | print(f"Success: {success}, Code: {code}") 51 | 52 | # Check orderbook 53 | print("\n📋 Checking Orderbook...") 54 | orders = SandboxOrders.query.filter_by(user_id=user_id).all() 55 | 56 | print(f"Found {len(orders)} order(s):") 57 | for order in orders: 58 | print(f"\n Order ID: {order.orderid}") 59 | print(f" Symbol: {order.symbol}") 60 | print(f" Action: {order.action}") 61 | print(f" Quantity: {order.quantity}") 62 | print(f" Product: {order.product}") 63 | print(f" Status: {order.order_status}") 64 | print(f" Rejection Reason: {order.rejection_reason}") 65 | print(f" Margin Blocked: {order.margin_blocked}") 66 | 67 | if orders and orders[0].order_status == 'rejected': 68 | print("\n✅ SUCCESS: Rejected order appears in orderbook!") 69 | else: 70 | print("\n❌ FAIL: Rejected order not in orderbook") 71 | 72 | print("="*60) -------------------------------------------------------------------------------- /broker/samco/api/funds.py: -------------------------------------------------------------------------------- 1 | # api/funds.py 2 | 3 | import os 4 | import json 5 | from utils.httpx_client import get_httpx_client 6 | from utils.logging import get_logger 7 | 8 | logger = get_logger(__name__) 9 | 10 | # Samco API base URL 11 | BASE_URL = "https://tradeapi.samco.in" 12 | 13 | 14 | def get_margin_data(auth_token): 15 | """Fetch margin data from Samco's API using the provided auth token.""" 16 | 17 | # Get the shared httpx client with connection pooling 18 | client = get_httpx_client() 19 | 20 | headers = { 21 | 'Accept': 'application/json', 22 | 'x-session-token': auth_token 23 | } 24 | 25 | response = client.get( 26 | f"{BASE_URL}/limit/getLimits", 27 | headers=headers 28 | ) 29 | 30 | # Add status attribute for compatibility with the existing codebase 31 | response.status = response.status_code 32 | 33 | margin_data = response.json() 34 | 35 | logger.info(f"Samco Margin Data: {margin_data}") 36 | 37 | if margin_data.get('status') == 'Success': 38 | equity_limit = margin_data.get('equityLimit', {}) 39 | commodity_limit = margin_data.get('commodityLimit', {}) 40 | 41 | # Calculate total available margin from equity and commodity 42 | equity_available = float(equity_limit.get('netAvailableMargin', 0) or 0) 43 | commodity_available = float(commodity_limit.get('netAvailableMargin', 0) or 0) 44 | 45 | equity_used = float(equity_limit.get('marginUsed', 0) or 0) 46 | commodity_used = float(commodity_limit.get('marginUsed', 0) or 0) 47 | 48 | # Map Samco fields to OpenAlgo standard format 49 | filtered_data = { 50 | 'availablecash': "{:.2f}".format(equity_available + commodity_available), 51 | 'collateral': "{:.2f}".format( 52 | float(equity_limit.get('collateralMarginAgainstShares', 0) or 0) + 53 | float(commodity_limit.get('collateralMarginAgainstShares', 0) or 0) 54 | ), 55 | 'm2mrealized': "{:.2f}".format(0), # Not provided by Samco 56 | 'm2munrealized': "{:.2f}".format(0), # Not provided by Samco 57 | 'utiliseddebits': "{:.2f}".format(equity_used + commodity_used) 58 | } 59 | return filtered_data 60 | else: 61 | logger.error(f"Samco margin data fetch failed: {margin_data.get('statusMessage', 'Unknown error')}") 62 | return {} 63 | -------------------------------------------------------------------------------- /broker/shoonya/api/auth_api.py: -------------------------------------------------------------------------------- 1 | import httpx 2 | import hashlib 3 | import json 4 | import os 5 | from utils.httpx_client import get_httpx_client 6 | 7 | def sha256_hash(text): 8 | """Generate SHA256 hash.""" 9 | return hashlib.sha256(text.encode('utf-8')).hexdigest() 10 | 11 | def authenticate_broker(userid, password, totp_code): 12 | """ 13 | Authenticate with Shoonya and return the auth token. 14 | """ 15 | # Get the Shoonya API key and other credentials from environment variables 16 | api_secretkey = os.getenv('BROKER_API_SECRET') 17 | vendor_code = os.getenv('BROKER_API_KEY') 18 | #imei = '1234567890abcdef' # Default IMEI if not provided 19 | imei = 'abc1234' # Default IMEI if not provided 20 | 21 | try: 22 | # Shoonya API login URL 23 | url = "https://api.shoonya.com/NorenWClientTP/QuickAuth" 24 | 25 | # Prepare login payload 26 | payload = { 27 | "uid": userid, # User ID 28 | "pwd": sha256_hash(password), # SHA256 hashed password 29 | "factor2": totp_code, # PAN or TOTP or DOB (second factor) 30 | "apkversion": "1.0.0", # API version (as per Shoonya's requirement) 31 | "appkey": sha256_hash(f"{userid}|{api_secretkey}"), # SHA256 of uid and API key 32 | "imei": imei, # IMEI or MAC address 33 | "vc": vendor_code, # Vendor code 34 | "source": "API" # Source of login request 35 | } 36 | 37 | # Convert payload to string with 'jData=' prefix 38 | payload_str = "jData=" + json.dumps(payload) 39 | 40 | # Set headers for the API request 41 | headers = { 42 | 'Content-Type': 'application/x-www-form-urlencoded' 43 | } 44 | 45 | # Get the shared httpx client and send the POST request to Shoonya's API 46 | client = get_httpx_client() 47 | response = client.post(url, data=payload_str, headers=headers) 48 | 49 | # Handle the response 50 | if response.status_code == 200: 51 | data = response.json() 52 | if data['stat'] == "Ok": 53 | return data['susertoken'], None # Return the token on success 54 | else: 55 | return None, data.get('emsg', 'Authentication failed. Please try again.') 56 | else: 57 | return None, f"Error: {response.status_code}, {response.text}" 58 | 59 | except Exception as e: 60 | return None, str(e) -------------------------------------------------------------------------------- /test/test_log_location.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | """ 3 | Test script to verify log location change 4 | """ 5 | 6 | import os 7 | from pathlib import Path 8 | 9 | def test_log_location(): 10 | """Verify log folder structure is correct""" 11 | 12 | print("Testing Log Location Configuration") 13 | print("-" * 40) 14 | 15 | # Check log folder exists 16 | log_dir = Path('log') 17 | if log_dir.exists(): 18 | print(f"[OK] Log directory exists: {log_dir.absolute()}") 19 | else: 20 | print(f"[ERROR] Log directory missing: {log_dir.absolute()}") 21 | return False 22 | 23 | # Check strategies subfolder 24 | strategies_log_dir = log_dir / 'strategies' 25 | if strategies_log_dir.exists(): 26 | print(f"[OK] Strategies log directory exists: {strategies_log_dir.absolute()}") 27 | else: 28 | print(f"[ERROR] Strategies log directory missing: {strategies_log_dir.absolute()}") 29 | return False 30 | 31 | # Check gitignore file 32 | gitignore_file = strategies_log_dir / '.gitignore' 33 | if gitignore_file.exists(): 34 | print(f"[OK] .gitignore file exists: {gitignore_file.absolute()}") 35 | 36 | # Check gitignore content 37 | with open(gitignore_file, 'r') as f: 38 | content = f.read() 39 | if '*.log' in content: 40 | print("[OK] .gitignore correctly ignores log files") 41 | else: 42 | print("[ERROR] .gitignore missing *.log pattern") 43 | else: 44 | print(f"[ERROR] .gitignore file missing: {gitignore_file.absolute()}") 45 | return False 46 | 47 | # Check old logs folder is removed 48 | old_logs_dir = Path('logs') 49 | if not old_logs_dir.exists(): 50 | print(f"[OK] Old logs directory removed") 51 | else: 52 | print(f"[WARNING] Old logs directory still exists: {old_logs_dir.absolute()}") 53 | print(" Please remove it manually") 54 | 55 | print("-" * 40) 56 | print("[SUCCESS] Log location configuration is correct!") 57 | print("\nLog files will be saved to: log/strategies/") 58 | print("Example: log/strategies/strategy_id_20240101_120000_IST.log") 59 | 60 | return True 61 | 62 | if __name__ == "__main__": 63 | # Change to OpenAlgo root directory if needed 64 | if os.path.basename(os.getcwd()) == 'test': 65 | os.chdir('..') 66 | 67 | success = test_log_location() 68 | exit(0 if success else 1) --------------------------------------------------------------------------------