Backtesting.py - Simultaneous trading of stocks - python
I have a following code using python library backtest.py now I want to backtest it over 2 stocks namely (AAPL & MSFT) from 01-01-2022 until 01-12-2022 and I want to make sure that if I buy AAPL stock on 01-01-2022 and sell it on 01-03-2022 then in the next buy which will be after 01-03-2022 the below code will look for the buy signal in both stocks again before making a trade.
Please assist!
from backtesting import Backtest, Strategy
from backtesting.lib import crossover
from backtesting.test import SMA
class SmaCross(Strategy):
def init(self):
price = self.data.Close
self.ma1 = self.I(SMA, price, 10)
self.ma2 = self.I(SMA, price, 20)
def next(self):
if crossover(self.ma1, self.ma2):
self.buy()
elif crossover(self.ma2, self.ma1):
self.sell()
bt = Backtest(AAPL, SmaCross, commission=.002,
exclusive_orders=True)
stats = bt.run()
Related
Python - error importing Nasdaq stock list
Im preparing a python code , to screen stocks from the SP500 , DOW and Nasdaq. SP500 and DOW importing data stocks is working properly , but when I try to import Nasdaq always get similar error, related to timestamp. See below: My code: import talib from yahoo_fin.stock_info import get_data import yahoo_fin.stock_info as si from datetime import datetime list = si.tickers_nasdaq() # Get current date and time now = datetime.now().strftime("%m_%d_%Y_%I_%M_%S") end_date = datetime.now().strftime('%Y-%m-%d') start_date = (datetime.now() - timedelta(days=365)).strftime('%Y-%m-%d') # Create file to save results f = open(f'C:/Users/fco_j/OneDrive/Escritorio/CHAT GPT/Python/Reports/dow_results_{now}.csv', 'w') # print table header to file f.write('Ticker, ClosePrice, SMA200, SMA20, RSI, RelVol\n') # Define cache_data function def cache_data(data, stock): data.to_pickle(f'C:/Users/fco_j/OneDrive/Escritorio/CHAT GPT/Python/Pickle/{stock}.pkl') for stock in list: # Download historical data for past year data = si.get_data(stock, start_date=start_date, end_date=end_date) last_price = data["close"][-1] # Get 150 and 20 simple moving averages using Talib sma150 = talib.SMA(data['close'], timeperiod=150)[-1] sma20 = talib.SMA(data['close'], timeperiod=20)[-1] rsi = talib.RSI(data['close'], timeperiod=14) # Calculate Relative Volume rel_vol = data['volume'] / talib.SMA(data['volume'].values.astype(float), timeperiod = 50) # Cache data cache_data(data, stock) # Filter stocks with relative volume (time period 20) over 1 if last_price > sma150 and last_price > sma20 and rsi[-1] > 50 and rel_vol[-1] > 1: # Print results to file f.write(f"{stock},{last_price},{sma150},{sma20},{rsi[-1]},{rel_vol[-1]}\n") f.close() The error: KeyError Traceback (most recent call last) ~\AppData\Local\Temp/ipykernel_11208/2663596324.py in 26 for stock in dow_list: 27 # Download historical data for past year ---> 28 data = si.get_data(stock, start_date=start_date, end_date=end_date) 29 last_price = data["close"][-1] 30 # Get 150 and 20 simple moving averages using Talib ~\anaconda3\envs\PyFinance\lib\site-packages\yahoo_fin\stock_info.py in get_data(ticker, start_date, end_date, index_as_date, interval, headers) 98 99 # get the date info --> 100 temp_time = data["chart"]["result"][0]["timestamp"] 101 102 if interval != "1m": KeyError: 'timestamp' The code is working with si.tickers_dow() and si.tickers_sp500() , but not with si.tickers_nasdaq() . Not sure if a dataframe issue.
linear programing: find max number of shares of stock to purchase given a budget
The problem I am working on is this: say i have 10k, and I want to buy 3 stocks: AMZN, CBOE, CDW. and my goal is the calculate the max number of shares I can purchase and stay within 10k. X*amzn_price+Y*cboe_price+Z*cdw_price <= 10000 I did the below code based on this example: from scipy.optimize import linprog import yfinance as yf def linearProblem(dict): obj = list(dict.values()) lhs_ineq = [obj] rhs_ineq = [10000] opt = linprog(c=obj, A_ub=lhs_ineq, b_ub=rhs_ineq, method = "revised simplex") # Press the green button in the gutter to run the script. def getPrice(): symbol_list = ['AMZN', 'CBOE', 'CDW'] symbol_list_dict = {} for symbol in symbol_list: ticker = yf.Ticker(symbol).info market_price = ticker['regularMarketPrice'] symbol_list_dict[symbol] = market_price return symbol_list_dict if __name__ == '__main__': symbol_price_dict = getPrice() linearProblem(symbol_price_dict) but the result is confusing with a fun of 0? I don't think I am inputting my equation right. {'AMZN': 88.45, 'CBOE': 124.14, 'CDW': 184.2} [88.45, 124.14, 184.2] 3 1 con: array([], dtype=float64) fun: 0.0 message: 'Optimization terminated successfully.' nit: 0 slack: array([10000.]) status: 0 success: True x: array([0., 0., 0.])
I have used Google-ortool's CP-SAT solver. Please have a look at the below solution. You may add more constraints based on your requirements. import string import random from ortools.sat.python import cp_model as cp stocks = list(string.ascii_lowercase) stock_price = {} for stk in stocks: stock_price[stk] = random.randint(1, 100) # decision variables dv_pick_stocks = {} for stk in stocks: dv_pick_stocks[stk] = model.NewBoolVar("") # constraint model.Add(sum(dv_pick_stocks[stk] * stock_price[stk] for stk in stocks) <= 500) # decision variable to hold number of stocks selected dv_num_stock_selected = model.NewIntVar(0, len(stocks), "") model.Add(dv_num_stock_selected == sum(dv_pick_stocks[stk] for stk in stocks)) model.Maximize(dv_num_stock_selected) solver = cp.CpSolver() solver.Solve(model) # inspect the solution solver.Value(dv_num_stock_selected) [(stk, stock_price[stk]) for stk in stocks if solver.Value(dv_pick_stocks[stk]) > 0]
Python - have a function output multiple data frames
I would like to achieve the following. I would like the function to output a separate data frame for each stock for example I would like to save each data frame to a separate csv the code doesnt work and i am unable to output the and save the separate csvs. Can you help me output separate dataframes and export the respective CSVS? def getdata(stock: str): # Company Quote Group of Items company_quote = requests.get(f"https://financialmodelingprep.com/api/v3/quote/{stock}?apikey=demo") company_quote = company_quote.json() share_price = float("{0:.2f}".format(company_quote[0]['price'])) # Balance Sheet Group of Items BS = requests.get(f"https://financialmodelingprep.com/api/v3/income-statement/{stock}?period=quarter&limit=400&apikey=demo") BS = BS.json() #Total Debt debt = float("{0:.2f}".format(float(BS[0]['totalDebt'])/10**9)) #Total Cash cash = float("{0:.2f}".format(float(BS[0]['cashAndShortTermInvestments'])/10**9)) # Income Statement Group of Items IS = requests.get(f"https://financialmodelingprep.com/api/v3/income-statement/{stock}?period=quarter&limit=400&apikey=demo") IS = IS.json() # Most Recent Quarterly Revenue qRev = float("{0:.2f}".format(float(IS[0]['revenue'])/10**9)) # Company Profile Group of Items company_info = requests.get(f"https://financialmodelingprep.com/api/v3/profile/{stock}?apikey=demo") company_info = company_info.json() # Chief Executive Officer ceo = (company_info[0]['ceo']) return (share_price, cash, debt, qRev, ceo) stocks = ('AAPL') d = {} for stock in stocks: df[stock] = pd.DataFrame(getdata, columns=['Share Price','Total Cash', 'Total Debt', 'Q3 2019 Revenue', 'CEO'], index=tickers) print(d)
I'm not sure to understand your question. But if the fact is that you want a different DataFrame for each ticker, here is a solution. Instead of : for stock in stocks: df[stock] = pd.DataFrame(.... Try: for stock in stocks: globals()['df_%s' %stock] = pd.DataFrame(... # And to save it, inside the loop globals()['df_%s' %stock].to_csv(stock+'.csv') EDIT: Thanx for the add. Here is the code import joblib from joblib import Parallel,delayed import requests import pandas as pd def getdata(stock): # Company Quote Group of Items company_quote = requests.get(f"https://financialmodelingprep.com/api/v3/quote/{stock}?apikey=demo") company_quote = company_quote.json() share_price = float("{0:.2f}".format(company_quote[0]['price'])) # Balance Sheet Group of Items BS = requests.get(f"https://financialmodelingprep.com/api/v3/balance-sheet-statement/{stock}?period=quarter&limit=400&apikey=demo") BS = BS.json() #Total Debt debt = float("{0:.2f}".format(float(BS[0]['totalDebt'])/10**9)) #Total Cash cash = float("{0:.2f}".format(float(BS[0]['cashAndShortTermInvestments'])/10**9)) # Income Statement Group of Items IS = requests.get(f"https://financialmodelingprep.com/api/v3/income-statement/{stock}?period=quarter&limit=400&apikey=demo") IS = IS.json() # Most Recent Quarterly Revenue qRev = float("{0:.2f}".format(float(IS[0]['revenue'])/10**9)) # Company Profile Group of Items company_info = requests.get(f"https://financialmodelingprep.com/api/v3/profile/{stock}?apikey=demo") company_info = company_info.json() # Chief Executive Officer ceo = (company_info[0]['ceo']) globals()['df_%s' %stock] = pd.DataFrame({'symbol':[stock],'debt':[debt],'cash':[cash],'qRev':[qRev],'ceo':[ceo]}) globals()['df_%s' %stock].to_csv(stock+'.csv') return(globals()['df_%s' %stock]) stocks = ['AAPL'] #, 'MSFT', 'GOOG', 'T', 'CSCO', 'INTC', 'ORCL', 'AMZN', 'FB', 'TSLA', 'NVDA'] number_of_cpu = joblib.cpu_count() delayed_funcs = [delayed(getdata)(stock) for stock in stocks] parallel_pool = Parallel(n_jobs=number_of_cpu,prefer="processes") # processes threads globals()['df_%s' %stock] = parallel_pool(delayed_funcs) df_AAPL OUTPUT It isn't necessary to return the DataFrame, as you save it in the function. But I did it to show you the possibility.
Improving a stock market algorithm
I am trying to make this code look more attractive for potential employers that view it on my GitHub account. The code essentially loops through a CSV file and searches each symbol with the yfinance wrapper for the Yahoo-Finance API. It makes a few checks about the stock and decides whether it is a suitable investment. There are many try except clauses since API can return empty fields in the pandas dataframe. Currently I think it can be improved since it has multiple nested if statements with many try except statements. All feedback is greatly appreciated. import yfinance as yf import pandas as pd import openpyxl import csv import math import traceback # Not a penny stock # Earnings increase of at least 33% over 10 years using 3 year averages - 10% over 4 years since the API only contains the most recent 4 years # Current price no more than 1.5x book value per share # P/E ratio <= 15 # Long term debt no more than 110% current assets # Current assets 1.5x current liabilities symbol_array = [] failed_search = [] with open('companylist.csv') as file: reader = csv.reader(file) ticker_data = iter(reader) # skip the first value since it is the header next(ticker_data) for row in ticker_data: ticker = row[0] print('Searching: ', ticker) try: try: company = yf.Ticker(ticker) company_info = company.info except: print('Not a company') continue # skip the ticker since it is not a company or the API doesn't have any information about the security company_balance_sheet = company.balance_sheet company_earnings = company.earnings if company_balance_sheet.empty or company_earnings.empty: continue # if balance sheets or earnings reports are not available, skip the search column_date = company.balance_sheet.columns[0] # latest date on balance sheet to take data from current_assets = company.balance_sheet.at['Total Current Assets', column_date] try: # previous close price can be under 'previousClose' or 'regularMarketPrice' in company_info current_price = company_info['previousClose'] except: current_price = company_info['regularMarketPrice'] if current_price >= 10: # check if stock is penny stock try: long_term_debt = company.balance_sheet.at['Long Term Debt', column_date] if math.isnan(long_term_debt): long_term_debt = 0 except: long_term_debt=0 if long_term_debt < (current_assets * 1.1): current_liabilities = company.balance_sheet.at['Total Current Liabilities', column_date] if current_liabilities < (1.5 * current_assets): try: pe_ratio = company_info['trailingPE'] # check if P/E ratio is available, assign pe_ratio 0 if it is not except: pe_ratio = 0 if pe_ratio <= 15: try: book_value = company_info['bookValue'] if type(book_value) != float: # book_value can be "None" in the company_info object book_value = 0 except: book_value = 0 if current_price < (book_value*1.5): earnings_first = company.earnings.iat[0, 1] earnings_last = company.earnings.iat[len(company.earnings)-1, 1] if earnings_last >= earnings_first*1.1: symbol_array.append(company_info['symbol']) else: print('Step 6 fail. Earnings growth too low') else: print('Step 5 fail. Current price too high') else: print('Step 4 fail. P/E ratio too high') else: print('Step 3 fail. Current liabilities too high') else: print('Step 2 fail. Long term debt too high') else: print('Step 1 fail. Penny stock') except Exception as e: print(traceback.format_exc()) # code to point out any errors in the main try statement failed_search.append(ticker) print(ticker, ' failed to search.') print(e) print('Failed searches:') for failure in failed_search: print(failure) print('Potential Investments:') for symbol in symbol_array: print(symbol)
Failing to obtain correct accrued interest with QuantLib Inflation Bond pricer in Python
I am trying to price an inflation bond using QuantLib-Python. I seem to be producing numbers very close to Reuters and Bloomberg when I compare my data, however, I can not get the accrued interest to match. Both Reuters and Bloomberg are showing 0.062230211, however, my code is producing 0.062285744677396664. I have tried modifying my daycount assumptions, settlement day assumptions and a few other things to no avail. import QuantLib as ql import datetime as dt import numpy as np import pandas as pd calendar = ql.UnitedKingdom() lag = 3 today = ql.Date(23, 8, 2019) evaluationDate = calendar.advance(today, 1, ql.Days) dayCounter = ql.ActualActual(ql.ActualActual.ISMA) convention = ql.ModifiedFollowing lag = 3 # ISIN: GB00BDX8CX86 # inflation fixing lag period (3mths for gilts) issue_date = ql.Date(25, 9, 2013) maturity_date = ql.Date(22, 3, 2068) fixing_date = calendar.advance(evaluationDate,-lag, ql.Months) observationLag = ql.Period(lag, ql.Months) # assign evaluation date ql.Settings.instance().setEvaluationDate(evaluationDate) # derives a quantlib yield term structure for discounting yTS = ql.YieldTermStructureHandle(ql.FlatForward(evaluationDate, 0.01033692, dayCounter)) # define period gap tenor = ql.Period(1, ql.Months) # define RPI schedule within Quantlib from_date = ql.Date(31, ql.August, 2018); to_date = ql.Date(31, ql.July, 2019); convention = ql.Unadjusted rpiSchedule = ql.Schedule(from_date, to_date, tenor, calendar, convention, convention, ql.DateGeneration.Backward, True) # manually input RPI fixings for now fixData = [284.2, 284.1, 284.5, 284.6, 285.6, 283, 285, 285.1, 288.2, 289.2, 289.6, 289.5] dte_fixings=[dtes for dtes in rpiSchedule] # this is the going to be holder of the zero swap inflation curve. cpiTS = ql.RelinkableZeroInflationTermStructureHandle() inflationIndex = ql.UKRPI(False, cpiTS) #current interpolated RPI rate fixing_rate = 289.53548 inflationIndex.addFixing(fixing_date, fixing_rate) zciisData = [(ql.Date(31,7,2020), 3.1500000000137085), (ql.Date(31,7,2021), 3.547500000013759), (ql.Date(31,7,2022), 3.675000000013573), (ql.Date(31,7,2023), 3.7250000000134342), (ql.Date(31,7,2024), 3.750000000013265), (ql.Date(31,7,2025), 3.7430000000129526), (ql.Date(31,7,2026), 3.741200000012679), (ql.Date(31,7,2027), 3.7337000000123632), (ql.Date(31,7,2028), 3.725000000011902), (ql.Date(31,7,2029), 3.720000000011603), (ql.Date(31,7,2030), 3.712517289063011), (ql.Date(31,7,2031), 3.7013000000108764), (ql.Date(31,7,2032), 3.686986039205209), (ql.Date(31,7,2033), 3.671102614032895), (ql.Date(31,7,2034), 3.655000000009778), (ql.Date(31,7,2035), 3.6394715951305834), (ql.Date(31,7,2036), 3.624362044800966), (ql.Date(31,7,2037), 3.6093619727979087), (ql.Date(31,7,2038), 3.59421438364369), (ql.Date(31,7,2039), 3.5787000000081948), (ql.Date(31,7,2040), 3.5626192748395624), (ql.Date(31,7,2041), 3.545765016376823), (ql.Date(31,7,2042), 3.527943521613608), (ql.Date(31,7,2043), 3.508977137925462), (ql.Date(31,7,2044), 3.48870000000685), (ql.Date(31,7,2045), 3.467083068721011), (ql.Date(31,7,2046), 3.4445738220594935), (ql.Date(31,7,2047), 3.4216470902302065), (ql.Date(31,7,2048), 3.3986861494999188), (ql.Date(31,7,2049), 3.376000000005752), (ql.Date(31,7,2050), 3.3538412080641233), (ql.Date(31,7,2051), 3.3324275806807746), (ql.Date(31,7,2052), 3.311938788306623), (ql.Date(31,7,2053), 3.2925208131865835), (ql.Date(31,7,2054), 3.274293040759302), (ql.Date(31,7,2055), 3.2573541974782794), (ql.Date(31,7,2056), 3.241787355503245), (ql.Date(31,7,2057), 3.227664186159851), (ql.Date(31,7,2058), 3.2150486140060774), (ql.Date(31,7,2059), 3.204000000004159), (ql.Date(31,7,2060), 3.1945334946674064), (ql.Date(31,7,2061), 3.1865047145143377), (ql.Date(31,7,2062), 3.179753073456304), (ql.Date(31,7,2063), 3.1741427790361154), (ql.Date(31,7,2064), 3.1695593261025223), (ql.Date(31,7,2065), 3.1659065919088736), (ql.Date(31,7,2066), 3.163104428386987), (ql.Date(31,7,2067), 3.1610866681252903), (ql.Date(31,7,2068), 3.1597994770515836), (ql.Date(31,7,2069), 3.159200000003204), (ql.Date(31,7,2070), 3.159242349440139), (ql.Date(31,7,2071), 3.1598400898057433), (ql.Date(31,7,2072), 3.16090721831932), (ql.Date(31,7,2073), 3.162369676612098), (ql.Date(31,7,2074), 3.1641636543027207)] # bootstrap the inflation swap curve (quantlib) zeroSwapHelpers = [ql.ZeroCouponInflationSwapHelper(rate / 100,observationLag, date, calendar, convention, dayCounter, inflationIndex) for date,rate in zciisData] # the derived inflation curve jj=ql.PiecewiseZeroInflation(evaluationDate, calendar, dayCounter, observationLag,\ inflationIndex.frequency(), inflationIndex.interpolated(),\ zciisData[0][1],#baseZeroRate,\ yTS, zeroSwapHelpers, 1.0e-12, ql.Linear()) cpiTS.linkTo(jj) notional = 1000000 # real coupon of bond fixedRates = [0.125 / 100] # bond settings fixedDayCounter = ql.ActualActual(ql.ActualActual.ISMA) fixedPaymentConvention = ql.ModifiedFollowing fixedPaymentCalendar = ql.UnitedKingdom() contractObservationLag = ql.Period(3, ql.Months) # how do you observe the index? as-is, AsIndex, Flat, Linear? observationInterpolation = ql.CPI.Flat settlementDays = 1 # Boolean switch # - only price elements of bond that appreciate in value due to inflation growthOnly = False # base CPI fixing (generated on bond issue date) baseCPI = 249.7 # generate quantlib schedule fixedSchedule = ql.Schedule(issue_date, maturity_date, ql.Period(ql.Semiannual), fixedPaymentCalendar, ql.ModifiedFollowing, ql.ModifiedFollowing, ql.DateGeneration.Backward, False) # create bond within quantlib library bond = ql.CPIBond(settlementDays, notional, growthOnly, baseCPI, contractObservationLag, inflationIndex, observationInterpolation, fixedSchedule, fixedRates, fixedDayCounter, fixedPaymentConvention) # assign bond pricing engine and set pricing within quantlib bondEngine=ql.DiscountingBondEngine(yTS) bond.setPricingEngine(bondEngine) # calc bond yield print(bond.NPV()) print(bond.cleanPrice()) compounding = ql.Compounded yield_rate = bond.bondYield(fixedDayCounter,compounding,ql.Semiannual) y_curve = ql.InterestRate(yield_rate,fixedDayCounter,compounding,ql.Semiannual) print(y_curve) ##Collate results print("Clean Price:", bond.cleanPrice()) print("Dirty Price:", bond.dirtyPrice()) print("Notional:", bond.notional()) print("Yield:", yield_rate) print("Accrual Period", ql.BondFunctions.accrualPeriod(bond, ql.Date(27,8,2019))) print("Accrued Period", ql.BondFunctions.accruedPeriod(bond, ql.Date(27,8,2019))) print("Accrued Days", ql.BondFunctions.accruedDays(bond, ql.Date(27,8,2019))) print("Accrued Amount:", bond.accruedAmount()) print("Settlement Value:", bond.settlementValue()) Expecting accrued interest : 0.062230211 Actual accrued interest: 0.062285744677396664