diff --git a/.gitignore b/.gitignore index c8ef175..ed8ebf5 100644 --- a/.gitignore +++ b/.gitignore @@ -1,2 +1 @@ -__pycache__ -config.py \ No newline at end of file +__pycache__ \ No newline at end of file diff --git a/README.md b/README.md index cf28921..03d1d7d 100644 --- a/README.md +++ b/README.md @@ -14,6 +14,20 @@ # HFT-EXT > An extensible framework for high-frequency trading built on top of [Alpaca](https://alpaca.markets/) and Yahoo Finance. +## Table of Contents +- [HFT-EXT](#hft-ext) + - [Table of Contents](#table-of-contents) + - [Disclaimer](#disclaimer) + - [Why should I use HFT-EXT?](#why-should-i-use-hft-ext) + - [I'm sold! How do I get started?](#im-sold-how-do-i-get-started) + - [I have a working strategy! How do I deploy it to live trading?](#i-have-a-working-strategy-how-do-i-deploy-it-to-live-trading) + - [How do I add my own strategies?](#how-do-i-add-my-own-strategies) + - [What about if I want to change the amount of shares I buy for each pricepoint?](#what-about-if-i-want-to-change-the-amount-of-shares-i-buy-for-each-pricepoint) + - [This is great, but I wish I could customize which tickers I want to trade.](#this-is-great-but-i-wish-i-could-customize-which-tickers-i-want-to-trade) + - [I want to get to modifying the framework, but I don't know where to start.](#i-want-to-get-to-modifying-the-framework-but-i-dont-know-where-to-start) + - [I want to contribute!](#i-want-to-contribute) + + ## Disclaimer _It goes without saying that past performance is not indicative of future results. This framework is not intended to be used for live trading. If you do use it for live trading, you do so at your own risk. The authors of this framework are not responsible for any losses incurred by using this framework. Please use this framework responsibly._ @@ -33,7 +47,15 @@ Then, you need to install the dependencies. You can do this by running the follo cd hft-ext pip install -r requirements.txt ``` -Finally, you need to add your API keys to the `config.example.py` file. Simply fill the `ALPACA_PUBLIC_KEY` and `ALPACA_SECRET_KEY` variables with your API keys. Then, rename the file to `config.py`. You can now run the framework by running the following command in your terminal: +Finally, you need to add your API keys to the `config.py` file. We have a built-in script that will help you do this. You can run this script by running the following command in your terminal: +```bash +cd helpers +python auth.py +``` +The script should look like this: +![auth-example](images/auth-example.png) + +From there, make sure you are back in the root directory ``cd ..`` and run the following command in your terminal: ```bash python main.py ``` @@ -68,8 +90,25 @@ strategy = Strategy(ArimaStrategy()) The default strategy used is the ARIMA strategy. You can find the code for this strategy in the `scripts/strategies.py` file, within the `ArimaStrategy` class. +## What about if I want to change the amount of shares I buy for each pricepoint? +By default, the model will buy a total of $200 worth of shares for each pricepoint. We recognize that this may be a bit too much for some people, so we have made it easy to change this. You can run the following command in your terminal to change the amount of shares you buy for each pricepoint: +```bash +cd helpers +python amount.py +``` +The script should look like this: +![amount-example](images/amount-example.png) +Which is also the default setting. + + ## This is great, but I wish I could customize which tickers I want to trade. -You can customize which tickers you want to trade by adding them to the `config.py` file. Simply add the tickers you want to trade to the `TRADE_TICKERS` variable. For example, if you want to trade Apple, Microsoft, and Tesla, you would add `TRADE_TICKERS = ["AAPL", "MSFT", "TSLA"]` to the `config.py` file. However, it is recommended that you use the default tickers, as they are the ones that have been tested and are known to work well with the framework. +You can customize which tickers you want to trade by adding them to the `config.py` file. We have a built-in script that will help you do this. You can run this script by running the following command in your terminal: +```bash +cd helpers +python tickers.py +``` +The script should look like this: +![tickers-example](images/tickers-example.png) As an aside, there are no limits to the number of tickers you can trade. However, the more tickers you trade, the more you risk running into API rate limits. If you do run into API rate limits, you can simply wait a few minutes and try again. If you want to avoid this, you can simply trade fewer tickers. diff --git a/config.example.py b/config.example.py deleted file mode 100644 index 348990b..0000000 --- a/config.example.py +++ /dev/null @@ -1,54 +0,0 @@ -ALPACA_PUBLIC_KEY = "" -ALPACA_SECRET_KEY = "" -PAPER = True - -# list of large cap stocks to trade -TRADE_TICKERS = [ - "AAPL", "MSFT", "AMZN", "GOOG", "FB", "JPM", "JNJ", "V", "PG", - "UNH", "MA", "HD", "VZ", "DIS", "BAC", "INTC", "T", "PFE", "CMCSA", - "WFC", "KO", "PEP", "NFLX", "NVDA", "ADBE", "CRM", "TMO", "CSCO", "ABT", - "NKE", "MRK", "MDT", "ACN", "COST", "AVGO", "TXN", "QCOM", "UNP", "NEE", - "PYPL", "LIN", "PM", "CVX", "MCD", "ORCL", "UPS", "IBM", "LOW", "MMM", - "AMGN", "GS", "BA", "CAT", "XOM", "WMT", "DHR", "AMT", "AXP", "HON", - "CVS", "BKNG", "CHTR", "SBUX", "GILD", "MDLZ", "BLK", "INTU", "TGT", - "ZTS", "WBA", "MU", "GE", "MCK", "GPN", "FIS", "USB", "DUK", "DOW", - "PLD", "RTX", "ANTM", "DE", "ISRG", "SYK", "LMT", "CI", "MS", "TJX", - "BK", "C", "PNC", "SPGI", "ADP", "CL", "SO", "CME", "COP", "MDXG", - "CNC", "CARR", "CRL", "CPB", "COF", "CAH", "KMX", "KHC", "K", "KEY", - "KMB", "KIM", "KMI", "KLAC", "KSS", "KDP", "KR", "LB", "LHX", "LH", - "LRCX", "LW", "LVS", "LEG", "LDOS", "LEN", "LLY", "LNC", "LIN", "LYB", - "LKQ", "LMT", "L", "LOW", "LUMN", "LYV", "MTB", "MRO", "MPC", "MKTX", - "MAR", "MMC", "MLM", "MAS", "MA", "MKC", "MXIM", "MCD", "MCK", "MDT", - "MRK", "MET", "MTD", "MGM", "MCHP", "MU", "MSFT", "MAA", "MHK", "TAP", - "MDLZ", "MNST", "MCO", "MS", "MOS", "MSI", "MSCI", "MYL", "NDAQ", "NOV", - "NTAP", "NFLX", "NWL", "NEM", "NWSA", "NWS", "NEE", "NLSN", "NKE", - "NI", "NSC", "NTRS", "NOC", "NLOK", "NCLH", "NRG", "NUE", "NVDA", - "NVR", "ORLY", "OXY", "ODFL", "OMC", "OKE", "ORCL", "PCAR", "PKG", - "PH", "PAYX", "PAYC", "PYPL", "PNR", "PBCT", "PEP", "PKI", "PRGO", - "PFE", "PM", "PSX", "PNW", "PXD", "PNC", "POOL", "PPG", "PPL", "PFG", - "PG", "PGR", "PLD", "PRU", "PTC", "PEG", "PSA", "PHM", "PVH", "QRVO", - "PWR", "QCOM", "DGX", "RL", "RJF", "RTX", "O", "REG", "REGN", "RF", - "RSG", "RMD", "RHI", "ROK", "ROL", "ROP", "ROST", "RCL", "SPGI", - "CRM", "SBAC", "SLB", "STX", "SEE", "SRE", "NOW", "SHW", "SPG", "SWKS", - "SLG", "SNA", "SO", "LUV", "SWK", "SBUX", "STT", "STE", "SYK", "SIVB", - "SYF", "SNPS", "SYY", "TMUS", "TROW", "TTWO", "TPR", "TGT", "TEL", - "TDY", "TFX", "TER", "TSLA", "TXN", "TXT", "TMO", "TJX", "TSCO", - "TT", "TDG", "TRV", "TRMB", "TFC", "TWTR", "TYL", "TSN", "UDR", "ULTA", - "USB", "UAA", "UA", "UNP", "UAL", "UNH", "UPS", "URI", "UHS", "UNM", - "VLO", "VTR", "VRSN", "VRSK", "VZ", "VRTX", "VFC", "VIAC", "VTRS", - "V", "VNO", "VMC", "WRB", "WAB", "WMT", "WBA", "DIS", "WM", "WAT", - "WEC", "WFC", "WELL", "WST", "WDC", "WU", "WRK", "WY", "WHR", "WMB", - "WLTW", "WYNN", "XEL", "XRX", "XYL", "YUM", "ZBRA", "ZBH", - "ZION", "ZTS" -] - -# API is limited to 200 requests per minute -ALPACA_TIMEOUT = 3.0 - - -def CHOOSE_AMOUNT(value): - value_buys = {1: 200, 5: 40, 10: 20, 20: 10, 50: 4, 100: 2} - for key in value_buys: - if value <= key: - return value_buys[key] - return 1 diff --git a/config.py b/config.py new file mode 100644 index 0000000..7943be9 --- /dev/null +++ b/config.py @@ -0,0 +1,37 @@ +# These are our API keys and other configuration settings +# for the Alpaca API and the trading algorithm. +# ALPACA_PUBLIC_KEY is the API key ID for your account. +# ALPACA_SECRET_KEY is the "passphrase" for your API key. +# PAPER is a boolean value that determines whether we are +# using the paper trading API or the live trading API. +# You can configure these values by running the helpers/auth.py script. +ALPACA_PUBLIC_KEY = "" +ALPACA_SECRET_KEY = "" +PAPER = True + +# ALPACA_TIMEOUT is the number of seconds we will wait for +# to avoid rate limiting. This same value is used for both +# Yahoo Finance and Alpaca. It defaults to 3 seconds. +ALPACA_TIMEOUT = 3.0 + +# SHARES_BY_PRICE is a dictionary that maps the price of a +# share to the number of shares we want to buy at that price. +# For example, we want to buy 200 shares at $1, 100 shares +# at $2, 40 shares at $5, 20 shares at $10, 10 shares at $20, ... +# You can configure this dictionary by running the helpers/amount.py script. +SHARES_BY_PRICE = {1: 200, 2: 100, 5: 40, 10: 20, 50: 4, 100: 2} + +# TRADE_TICKERS is a list of the tickers we want to trade. +# We will use the first 100 tickers in the S&P 500 by default. +# You can change this list to any other tickers you want. +# You can configure this list by running the helpers/tickers.py script. +TRADE_TICKERS = [ "AAPL", "MSFT", "AMZN", "GOOG", "FB", "JPM", "JNJ", "V", "PG", "UNH", "MA", "HD", "VZ", "DIS", "BAC", "INTC", "T", "PFE", "CMCSA", "WFC", "KO", "PEP", "NFLX", "NVDA", "ADBE", "CRM", "TMO", "CSCO", "ABT", "NKE", "MRK", "MDT", "ACN", "COST", "AVGO", "TXN", "QCOM", "UNP", "NEE", "PYPL", "LIN", "PM", "CVX", "MCD", "ORCL", "UPS", "IBM", "LOW", "MMM", "AMGN", "GS", "BA", "CAT", "XOM", "WMT", "DHR", "AMT", "AXP", "HON", "CVS", "BKNG", "CHTR", "SBUX", "GILD", "MDLZ", "BLK", "INTU", "TGT", "ZTS", "WBA", "MU", "GE", "MCK", "GPN", "FIS", "USB", "DUK", "DOW", "PLD", "RTX", "ANTM", "DE", "ISRG", "SYK", "LMT", "CI", "MS", "TJX", "BK", "C", "PNC", "SPGI", "ADP", "CL", "SO", "CME", "COP", "MDXG", "CNC", "CARR", "CRL", "CPB", "COF", "CAH", "KMX", "KHC", "K", "KEY", "KMB", "KIM", "KMI", "KLAC", "KSS", "KDP", "KR", "LB", "LHX", "LH", "LRCX", "LW", "LVS", "LEG", "LDOS", "LEN", "LLY", "LNC", "LIN", "LYB", "LKQ", "LMT", "L", "LOW", "LUMN", "LYV", "MTB", "MRO", "MPC", "MKTX", "MAR", "MMC", "MLM", "MAS", "MA", "MKC", "MXIM", "MCD", "MCK", "MDT", "MRK", "MET", "MTD", "MGM", "MCHP", "MU", "MSFT", "MAA", "MHK", "TAP", "MDLZ", "MNST", "MCO", "MS", "MOS", "MSI", "MSCI", "MYL", "NDAQ", "NOV", "NTAP", "NFLX", "NWL", "NEM", "NWSA", "NWS", "NEE", "NLSN", "NKE", "NI", "NSC", "NTRS", "NOC", "NLOK", "NCLH", "NRG", "NUE", "NVDA", "NVR", "ORLY", "OXY", "ODFL", "OMC", "OKE", "ORCL", "PCAR", "PKG", "PH", "PAYX", "PAYC", "PYPL", "PNR", "PBCT", "PEP", "PKI", "PRGO", "PFE", "PM", "PSX", "PNW", "PXD", "PNC", "POOL", "PPG", "PPL", "PFG", "PG", "PGR", "PLD", "PRU", "PTC", "PEG", "PSA", "PHM", "PVH", "QRVO", "PWR", "QCOM", "DGX", "RL", "RJF", "RTX", "O", "REG", "REGN", "RF", "RSG", "RMD", "RHI", "ROK", "ROL", "ROP", "ROST", "RCL", "SPGI", "CRM", "SBAC", "SLB", "STX", "SEE", "SRE", "NOW", "SHW", "SPG", "SWKS", "SLG", "SNA", "SO", "LUV", "SWK", "SBUX", "STT", "STE", "SYK", "SIVB", "SYF", "SNPS", "SYY", "TMUS", "TROW", "TTWO", "TPR", "TGT", "TEL", "TDY", "TFX", "TER", "TSLA", "TXN", "TXT", "TMO", "TJX", "TSCO", "TT", "TDG", "TRV", "TRMB", "TFC", "TWTR", "TYL", "TSN", "UDR", "ULTA", "USB", "UAA", "UA", "UNP", "UAL", "UNH", "UPS", "URI", "UHS", "UNM", "VLO", "VTR", "VRSN", "VRSK", "VZ", "VRTX", "VFC", "VIAC", "VTRS", "V", "VNO", "VMC", "WRB", "WAB", "WMT", "WBA", "DIS", "WM", "WAT", "WEC", "WFC", "WELL", "WST", "WDC", "WU", "WRK", "WY", "WHR", "WMB", "WLTW", "WYNN", "XEL", "XRX", "XYL", "YUM", "ZBRA", "ZBH", "ZION", "ZTS"] + +# CHOOSE_AMOUNT is a function that takes a price and returns +# the number of shares we want to buy at that price. It is not +# configurable, but you can change it if you want. +def CHOOSE_AMOUNT(value): + for key in SHARES_BY_PRICE: + if value <= key: + return SHARES_BY_PRICE[key] + return 1 diff --git a/helpers/amount.py b/helpers/amount.py new file mode 100644 index 0000000..c145d4a --- /dev/null +++ b/helpers/amount.py @@ -0,0 +1,43 @@ +class COLORS: + BLUE = "\033[94m" + END = "\033[0m" + RED = "\033[91m" + YELLOW = "\033[93m" + GREEN = "\033[92m" + + +def main(): + shares_by_price = {} + + # Ask the user to enter their preferred stock prices and corresponding shares to buy + while True: + try: + price = int( + # print blue text + input( + f"""{COLORS.BLUE}Enter a stock price for a new rule (or -1 to stop). +For example, entering 1000 will allow you to set the amount of shares to +buy when the stock price is $1000 or less:{COLORS.END} """ + ) + ) + if price == -1: + break + shares = int(input(f"{COLORS.YELLOW}Enter the number of shares to buy for this rule:{COLORS.END} ")) + shares_by_price[price] = shares + except ValueError: + print("Invalid input, please try again.") + + with open('../config.py', 'r') as f: + lines = f.readlines() + with open("../config.py", "w") as f: + for line in lines: + if line.startswith("SHARES_BY_PRICE"): + f.write(f"SHARES_BY_PRICE = {shares_by_price}\n") + else: + f.write(line) + + print(f"{COLORS.GREEN}Successfully updated config.py!{COLORS.END}") + + +if __name__ == "__main__": + main() diff --git a/helpers/auth.py b/helpers/auth.py new file mode 100644 index 0000000..c2fba8f --- /dev/null +++ b/helpers/auth.py @@ -0,0 +1,36 @@ +class COLORS: + BLUE = "\033[94m" + END = "\033[0m" + RED = "\033[91m" + YELLOW = "\033[93m" + GREEN = "\033[92m" + + +def main(): + try: + public = str(input(f"{COLORS.BLUE}Enter your Alpaca API Key ID:{COLORS.END} ")) + private = str(input(f"{COLORS.BLUE}Enter your Alpaca API Secret Key:{COLORS.END} ")) + paper = str(input(f"{COLORS.BLUE}Are you using a paper account? (y/n):{COLORS.END} ")) + except ValueError: + print("Invalid input, please try again.") + + with open("../config.py", "r") as f: + lines = f.readlines() + # Generate the dictionary and save it to the config file + with open("../config.py", "w") as f: + for line in lines: + if line.startswith("ALPACA_PUBLIC_KEY"): + f.write(f"ALPACA_PUBLIC_KEY = '{str(public)}'\n") + elif line.startswith("ALPACA_SECRET_KEY"): + f.write(f"ALPACA_SECRET_KEY = '{str(private)}'\n") + elif line.startswith("PAPER"): + boolean = paper.lower() == "y" or paper.lower() == "yes" + f.write(f"PAPER = {boolean}\n") + else: + f.write(line) + + print(f"{COLORS.GREEN}Successfully updated config.py!{COLORS.END}") + + +if __name__ == "__main__": + main() diff --git a/helpers/tickers.py b/helpers/tickers.py new file mode 100644 index 0000000..aa2e4b6 --- /dev/null +++ b/helpers/tickers.py @@ -0,0 +1,33 @@ +class COLORS: + BLUE = "\033[94m" + END = "\033[0m" + RED = "\033[91m" + YELLOW = "\033[93m" + GREEN = "\033[92m" + + +def main(): + tickers = [] + + while True: + try: + ticker = str(input(f"{COLORS.BLUE}Enter a ticker you would like to trade (or -1 to stop):{COLORS.END} ")) + if ticker == '-1': break + tickers.append(ticker) + except ValueError: + print("Invalid input, please try again.") + + with open("../config.py", "r") as f: + lines = f.readlines() + with open("../config.py", "w") as f: + for line in lines: + if line.startswith("TRADE_TICKERS"): + f.write(f"TRADE_TICKERS = {tickers}\n") + else: + f.write(line) + + print(f"{COLORS.GREEN}Successfully updated config.py!{COLORS.END}") + + +if __name__ == "__main__": + main() diff --git a/images/amount-example.png b/images/amount-example.png new file mode 100644 index 0000000..f945e0f Binary files /dev/null and b/images/amount-example.png differ diff --git a/images/auth-example.png b/images/auth-example.png new file mode 100644 index 0000000..61f6b67 Binary files /dev/null and b/images/auth-example.png differ diff --git a/images/tickers-example.png b/images/tickers-example.png new file mode 100644 index 0000000..0bbca92 Binary files /dev/null and b/images/tickers-example.png differ