{"id":13593016,"url":"https://github.com/alpacahq/example-scalping","last_synced_at":"2025-04-12T21:28:11.347Z","repository":{"id":36361677,"uuid":"213098996","full_name":"alpacahq/example-scalping","owner":"alpacahq","description":"A working example algorithm for scalping strategy trading multiple stocks concurrently using python asyncio","archived":false,"fork":false,"pushed_at":"2023-10-02T03:26:57.000Z","size":27,"stargazers_count":788,"open_issues_count":16,"forks_count":191,"subscribers_count":52,"default_branch":"master","last_synced_at":"2025-04-04T01:07:14.448Z","etag":null,"topics":["algorithmic-trading","algotrading","alpaca","api","async","asyncio","fintech","hft-trading","market-data","python3","scalping","trading-bot","trading-strategy"],"latest_commit_sha":null,"homepage":null,"language":"Python","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":null,"status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/alpacahq.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":null,"code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null}},"created_at":"2019-10-06T02:32:36.000Z","updated_at":"2025-04-03T15:26:45.000Z","dependencies_parsed_at":"2024-01-14T04:37:07.639Z","dependency_job_id":"790313f4-9d19-4eb4-a445-9fcdbf167030","html_url":"https://github.com/alpacahq/example-scalping","commit_stats":null,"previous_names":[],"tags_count":0,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/alpacahq%2Fexample-scalping","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/alpacahq%2Fexample-scalping/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/alpacahq%2Fexample-scalping/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/alpacahq%2Fexample-scalping/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/alpacahq","download_url":"https://codeload.github.com/alpacahq/example-scalping/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":248633890,"owners_count":21136932,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["algorithmic-trading","algotrading","alpaca","api","async","asyncio","fintech","hft-trading","market-data","python3","scalping","trading-bot","trading-strategy"],"created_at":"2024-08-01T16:01:15.689Z","updated_at":"2025-04-12T21:28:11.320Z","avatar_url":"https://github.com/alpacahq.png","language":"Python","funding_links":[],"categories":["Python"],"sub_categories":[],"readme":"# Concurrent Scalping Algo\n\nThis python script is a working example to execute \n[scalping trading](https://www.investopedia.com/articles/trading/05/scalping.asp) \nalgorithm for [Alpaca API](https://alpaca.markets). This algorithm uses real time order updates\nas well as minute level bar streaming from Polygon via Websockets (see the\n[document](https://docs.alpaca.markets/market-data/#consolidated-market-data) for\nPolygon data access).\nOne of the contributions of this example is to demonstrate how to handle\nmultiple stocks concurrently as independent routine using Python's\n[asyncio](https://docs.python.org/3/library/asyncio.html).\n\nThe strategy holds positions for very short period and exits positions quickly, so\nyou have to have more than $25k equity in your account due to the Pattern Day Trader rule,\nto run this example. For more information about PDT rule, please read the\n[document](https://support.alpaca.markets/hc/en-us/articles/360012203032-Pattern-Day-Trader).\n\n## Dependency\nThis script needs latest [Alpaca Python SDK](https://github.com/alpacahq/alpaca-trade-api-python).\nPlease install it using pip\n\n```sh\n$ pip3 install alpaca-trade-api\n```\n\nor use [pipenv](https://github.com/pypa/pipenv) using `Pipfile` in this directory.\n\n```sh\n$ pipenv install\n```\n\n## Usage\n\n```sh\n$ python main.py --lot=2000 TSLA FB AAPL\n```\n\nYou can specify as many symbols as you want.  The script is designed to kick off while market\nis open. Nothing would happen until 21 minutes from the market open as it relies on the\nsimple moving average as the buy signal.\n\n\n## Strategy\nThe algorithm idea is to buy the stock upon the buy signal (20 minute\n[moving average crossover](https://www.investopedia.com/articles/active-trading/052014/how-use-moving-average-buy-stocks.asp)) \nas much as `lot` amount of dollar, then immediately sell the position at or above the entry price.\nThe assumption is that the market is bouncing upward when this signal occurs in a short period of time.\nThe buy signal is extremely simple, but what this strategy achieves is the quick reaction to\nexit the position as soon as the buy order fills. There are reasonable probabilities that you can sell\nthe positions at the better prices than or the same price as your entry within the small window. We send\nlimit order at the last trade or position entry price whichever the higher to avoid unnecessary slippage.\n\nThe buy order is canceled after 2 minutes if it does not fill, assuming the signal is not\neffective anymore. This could happen in a fast-moving market situation. The sell order is left\nindifinitely until it fills, but this may cause loss more than the accumulated profit depending\non the market situation. This is where you can improve the risk control beyond this example.\n\nThe buy signal is calculated as soon as a minute bar arrives, which typically happen about 4 seconds\nafter the top of every minute (this is Polygon's behavior for minute bar streaming).\n\nThis example liquidates all watching positions with market order at the end of market hours (03:55pm ET).\n\n\n## Implementation\nThis example heavily relies on Python's asyncio. Although the thread is single, we handle\nmultiple symbols concurrently using this async loop.\n\nWe keep track of each symbol state in a separate `ScalpAlgo` class instance. That way,\neverything stays simple without complex data structure and easy to read. The `main()`\nfunction creates the algo instance for each symbol and creates streaming object\nto listen the bar events. As soon as we receive a minute bar, we invoke event handler\nfor each symbol.\n\nThe main routine also starts a period check routine to do some work in background every 30 seconds.\nIn this background task, we check market state with the clock API and liquidate positions\nbefore the market closes.\n\n### Algo Instance and State Management\nEach algo instance initializes its state by fetching day's bar data so far and position/order\nfrom Alpaca API to synchronize, in case the script restarts after some trades. There are\nfour internal states and transitions as events happen.\n\n- `TO_BUY`: no position, no order. Can transition to `BUY_SUBMITTED`\n- `BUY_SUBMITTED`: buy order has been submitted. Can transition to `TO_BUY` or `TO_SELL`\n- `TO_SELL`: buy is filled and holding position. Can transition to `SELL_SUBMITTED`\n- `SELL_SUBMITTED`: sell order has been submitted. Can transition to `TO_SELL` or `TO_BUY`\n\n### Event Handlers\n`on_bar()` is an event handler for the bar data. Here we calculate signal that triggers\na buy order in the `TO_BUY` state. Once order is submitted, it goes to the `BUY_SUBMITTED`\nstate.\n\nIf order is filled, `on_order_update()` handler is called with `event=fill`. The state\ntransitions to `TO_SELL` and immediately submits a sell order, to transition to the\n`SELL_SUBMITTED` state.\n\nOrders may be canceled or rejected (caused by this script or you manually cancel them\nfrom the dashboard). In these cases, the state transitions to `TO_BUY` (if not holding\na position) or `TO_SELL` (if holding a position) and wait for the next events.\n\n`checkup()` method is the background periodic job to check several conditions, where\nwe cancel open orders and sends market sell order if there is an open position.\n\nIt exits once the market closes.\n\n### Note\nEach algo instance owns its child logger, prefixed by the symbol name. The console\nlog is also emitted to a file `console.log` under the same directory for your later review.\n\nAgain, the beautify of this code is that there is no multithread code but each\nalgo instance can focus on the bar/order/position data only for its own. It still\nhandles multiple symbols concurrently plus runs background periodic job in the\nsame async loop.\n\nThe trick to run additional async routine is as follows.\n\n```py\n    loop = stream.loop\n    loop.run_until_complete(asyncio.gather(\n        stream.subscribe(channels),\n        periodic(),\n    ))\n    loop.close()\n```\n\nWe use `asyncio.gather()` to run all bar handler, order update handler and periodic job\nin one async loop indifinitely. You can kill it by `Ctrl+C`.\n\n### Customization\nInstead of using this buy signal of 20 minute simple moving average cross over, you can\nuse your own buy signal. To do so, extend the `ScalpAlgo` class and write your own\n`_calc_buy_signal()` method.\n\n```py\n    class MyScalpAlgo(ScalpAlgo):\n        def _calculate_buy_signal(self):\n            '''self._bars has all minute bars in the session so far. Return True to\n            trigger buy order'''\n            pass\n```\n\nAnd use it instead of the original class.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Falpacahq%2Fexample-scalping","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Falpacahq%2Fexample-scalping","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Falpacahq%2Fexample-scalping/lists"}