def __init__(self, acctNum): # Initialize Questrade Instance if path.exists("./access_token.yml"): #print("first try in questrade") self.qtrade = Questrade(token_yaml='./access_token.yml') try: acct_list = self.qtrade.get_account_id() #print(acctNum in acct_list) assert acctNum in acct_list except: #print("first if statement") self.qtrade.refresh_access_token(from_yaml=True) self.qtrade = Questrade(token_yaml='./access_token.yml') try: assert acctNum in self.qtrade.get_account_id() except: #print("yml file removed!") remove("./access_token.yml") # get new access code access_code = new_access_code() self.qtrade = Questrade(access_code=access_code) else: #print("no yml file exist") access_code = new_access_code() self.qtrade = Questrade(access_code=access_code) try: accts_list = self.qtrade.get_account_id() except: while not isinstance(accts_list, list): #print("in while loop") access_code = new_access_code() self.qtrade = Questrade(access_code=access_code) self.acctNum = acctNum
def test_refresh_token_non_yaml(mock_get): """This function tests the refresh token method without yaml use.""" qtrade = Questrade(token_yaml="access_token.yml") qtrade.refresh_access_token() assert set(qtrade.access_token.keys()) == set( ["access_token", "api_server", "expires_in", "refresh_token", "token_type"] ) assert qtrade.access_token["api_server"] == "https://questrade.api"
def test_refresh_token_non_yaml(mock_get): """This function tests the refresh token method. """ qtrade = Questrade(token_yaml='access_token.yml') qtrade.refresh_access_token(from_yaml=False) assert set(qtrade.access_token.keys()) == set(['access_token', 'api_server', 'expires_in', 'refresh_token', 'token_type']) assert qtrade.access_token['api_server'] == 'https://questrade.api'
def test_get_historical_data(mock_get): """This function tests the get historical data method.""" qtrade = Questrade(token_yaml="access_token.yml") historical_data = qtrade.get_historical_data("XYZ", "2018-08-01", "2018-08-02", "OneDay") assert len(historical_data) == 2 assert len(historical_data[0]) == 8 assert len(historical_data[1]) == 8 assert historical_data[0]["start"] == "2018-08-01T01:00:00.000000-04:00" assert historical_data[1]["start"] == "2018-08-02T00:00:00.000000-04:00"
def test_get_access_token(mock_get): """This function tests the get access token method. """ qtrade = Questrade(access_code='hunter2') assert set(qtrade.access_token.keys()) == set(['access_token', 'api_server', 'expires_in', 'refresh_token', 'token_type']) with pytest.raises(Exception) as e_info: _ = Questrade(access_code='hunter3') assert str(e_info.value) == 'Token type was not provided.'
def test_get_access_token(mock_get): """This function tests the get access token method.""" qtrade = Questrade(access_code="hunter2") assert set(qtrade.access_token.keys()) == set( ["access_token", "api_server", "expires_in", "refresh_token", "token_type"] ) with pytest.raises(Exception) as e_info: _ = Questrade(access_code="hunter3") assert str(e_info.value) == "Token type was not provided."
def test_get_historical_data(mock_get): """This function tests the get historical data method. """ qtrade = Questrade(token_yaml='access_token.yml') historical_data = qtrade.get_historical_data('XYZ', '2018-08-01', '2018-08-02', 'OneDay') assert len(historical_data) == 2 assert len(historical_data[0]) == 8 assert len(historical_data[1]) == 8 assert historical_data[0]['start'] == '2018-08-01T01:00:00.000000-04:00' assert historical_data[1]['start'] == '2018-08-02T00:00:00.000000-04:00'
def test_get_activity(mock_get): """This function tests the get account activities method.""" qtrade = Questrade(token_yaml="access_token.yml") activities = qtrade.get_account_activities(123, "2018-08-07", "2018-08-10") assert activities[0]["action"] == "Buy" assert activities[0]["tradeDate"] == "2018-08-07T00:00:00.000000-04:00" assert len(activities) == 1 assert len(activities[0]) == 14 with pytest.raises(Exception): _ = qtrade.get_account_activities(987, "2018-08-07", "2018-08-10")
def test_get_activity(mock_get): """This function tests the get account activities method. """ qtrade = Questrade(token_yaml='access_token.yml') activities = qtrade.get_account_activities(123, '2018-08-07', '2018-08-10') assert activities[0]['action'] == 'Buy' assert activities[0]['tradeDate'] == '2018-08-07T00:00:00.000000-04:00' assert len(activities) == 1 assert len(activities[0]) == 14 with pytest.raises(Exception): _ = qtrade.get_account_activities(987, '2018-08-07', '2018-08-10')
def get_cash_info(qt: Questrade, acct: int) -> str: bal_response = qt._send_message('get', 'accounts/' + str(acct) + '/balances') temp = qt._send_message('get', 'accounts/' + str(acct) + '/positions') print(temp) try: cash = round(bal_response['perCurrencyBalances'][0]['cash'], 2) except Exception: raise Exception result = '{F2}Cash{F1}: $' result += '{:.2f}'.format(cash) result += '{/F}' return result
def test_get_ticker_information(mock_get): """This function tests the get ticker information method.""" qtrade = Questrade(token_yaml="access_token.yml") ticker_info_single = qtrade.ticker_information("XYZ") assert len(ticker_info_single) == 34 assert ticker_info_single["symbol"] == "XYZ" assert ticker_info_single["marketCap"] == 275784564 ticker_info_multiple = qtrade.ticker_information(["XYZ", "ABC"]) assert len(ticker_info_multiple) == 2 assert len(ticker_info_multiple[0]) == 34 assert len(ticker_info_multiple[1]) == 34 assert ticker_info_multiple[0]["symbol"] == "XYZ"
def __init__(self, db=False, qtrade=False, ib=False): self.sql = SqlMapper() self.ib = IB() self.binance = Client() if qtrade: self.qtrade = Questrade( token_yaml= 'C:/Users/haseab/Desktop/Python/PycharmProjects/FAB/local/Workers/access_token.yml', save_yaml=True) print('Connected to Questrade API') if db: self.conn = self.sql.connect_psql() if ib: print(self.ib.connect('127.0.0.1', 7496, 104))
def test_get_positions(mock_get): """This function tests the get account positions method.""" qtrade = Questrade(token_yaml="access_token.yml") positions = qtrade.get_account_positions(123) assert positions[0]["symbol"] == "XYZ" assert positions[1]["symbol"] == "ABC" assert positions[0]["currentMarketValue"] == 3120 assert positions[1]["currentMarketValue"] == 4000 assert len(positions) == 2 assert len(positions[0]) == 12 assert len(positions[1]) == 12 with pytest.raises(Exception): _ = qtrade.get_account_positions(987)
def test_get_ticker_information(mock_get): """This function tests the get ticker information method. """ qtrade = Questrade(token_yaml='access_token.yml') ticker_info_single = qtrade.ticker_information('XYZ') assert len(ticker_info_single) == 34 assert ticker_info_single['symbol'] == 'XYZ' assert ticker_info_single['marketCap'] == 275784564 ticker_info_multiple = qtrade.ticker_information(['XYZ', 'ABC']) assert len(ticker_info_multiple) == 2 assert len(ticker_info_multiple[0]) == 34 assert len(ticker_info_multiple[1]) == 34 assert ticker_info_multiple[0]['symbol'] == 'XYZ'
def test_get_balances(mock_get): """This function tests the get account balances method.""" qtrade = Questrade(token_yaml="access_token.yml") balances = qtrade.get_account_balances(123) assert len(balances) == 4 assert list(balances.keys()) == [ "perCurrencyBalances", "combinedBalances", "sodPerCurrencyBalances", "sodCombinedBalances", ] with pytest.raises(Exception): _ = qtrade.get_account_positions(987)
def test_get_quote(mock_get): """This function tests the get quote method.""" qtrade = Questrade(token_yaml="access_token.yml") quote_single = qtrade.get_quote("XYZ") assert len(quote_single) == 21 assert quote_single["high52w"] == 25.00 assert quote_single["symbolId"] == 1234567 quote_multiple = qtrade.get_quote(["XYZ", "ABC"]) assert len(quote_multiple) == 2 assert len(quote_multiple[0]) == 21 assert len(quote_multiple[1]) == 21 assert quote_multiple[0]["high52w"] == 25.00 assert quote_multiple[1]["high52w"] == 25.00
def test_get_quote(mock_get): """This function tests the get quote method. """ qtrade = Questrade(token_yaml='access_token.yml') quote_single = qtrade.get_quote('XYZ') assert len(quote_single) == 21 assert quote_single['high52w'] == 25.00 assert quote_single['symbolId'] == 1234567 quote_multiple = qtrade.get_quote(['XYZ', 'ABC']) assert len(quote_multiple) == 2 assert len(quote_multiple[0]) == 21 assert len(quote_multiple[1]) == 21 assert quote_multiple[0]['high52w'] == 25.00 assert quote_multiple[1]['high52w'] == 25.00
def get_cash_info(qt: Questrade, acct: int) -> str: bal_response = qt._send_message('get', 'accounts/' + str(acct) + '/balances') out_csv_with_timestamp(CASH_OUTPUT, bal_response['perCurrencyBalances'].copy()); # temp = qt._send_message('get', 'accounts/' + str(acct) + '/positions') # print(temp); try: cash = round(bal_response['perCurrencyBalances'][0]['cash'], 2); except Exception: raise Exception; return F"{{F2}}Cash{{F1}}: ${cash:.2f}{{/F}}";
def test_init_via_yaml(): """This function tests when the class is initiated via yaml file. """ qtrade = Questrade(token_yaml='access_token.yml') assert set(qtrade.access_token.keys()) == set(['access_token', 'api_server', 'expires_in', 'refresh_token', 'token_type']) assert qtrade.access_token['access_token'] == 'hunter2' assert qtrade.access_token['api_server'] == 'www.api_url.com' assert qtrade.access_token['expires_in'] == 1234 assert qtrade.access_token['refresh_token'] == 'hunter2' assert qtrade.access_token['token_type'] == 'Bearer'
def test_init_via_yaml(): """This function tests when the class is initiated via yaml file.""" qtrade = Questrade(token_yaml="access_token.yml") assert set(qtrade.access_token.keys()) == set( ["access_token", "api_server", "expires_in", "refresh_token", "token_type"] ) assert qtrade.access_token["access_token"] == "hunter2" assert qtrade.access_token["api_server"] == "http://www.api_url.com" assert qtrade.access_token["expires_in"] == 1234 assert qtrade.access_token["refresh_token"] == "hunter2" assert qtrade.access_token["token_type"] == "Bearer"
class QuestradeClient: TICKER = "SPY" def __init__(self, token_yaml): self.yaml_path = token_yaml self.client = Questrade(token_yaml=token_yaml) self.refresh_access_token() def refresh_access_token(self): self.client.refresh_access_token(from_yaml=True, yaml_path=self.yaml_path) def get_nope(self): call_option_filters = [] put_option_filters = [] chain = self.client.get_option_chain(self.TICKER) quote = self.client.get_quote(self.TICKER) underlying_id = quote["symbolId"] for optionChain in chain["optionChain"]: exp_date = optionChain["expiryDate"] call_option_filters.append({ "optionType": "Call", "expiryDate": exp_date, "underlyingId": underlying_id, }) put_option_filters.append({ "optionType": "Put", "expiryDate": exp_date, "underlyingId": underlying_id, }) call_option_quotes = self.client.get_option_quotes( call_option_filters, []) put_option_quotes = self.client.get_option_quotes( put_option_filters, []) total_call_delta = sum( map(lambda q: q["volume"] * q["delta"], call_option_quotes["optionQuotes"])) total_put_delta = sum( map(lambda q: q["volume"] * q["delta"], put_option_quotes["optionQuotes"])) try: nope = ( (total_call_delta + total_put_delta) * 10000) / quote["volume"] except ZeroDivisionError: curr_dt = datetime.now().strftime("%Y-%m-%d at %H:%M:%S") with open("logs/errors.txt", "a") as f: f.write(f'No volume data on {quote["symbol"]} | {curr_dt}\n') return [0, 0] return [nope, quote["lastTradePrice"]]
def gen_string(): # Defining the token location. access_info = getenv('HOME') + '/.config/questrade/access_token.yml'; # Start the link using loaded conf. qt = Questrade(token_yaml=access_info); # we try up to three times to make requests. for _ in range(3): try: # Only have one account so we grab first entry. acct = qt.get_account_id()[0]; # Get the info for all positions held. positions = get_position_info(qt, acct); # Get the specific cash balance info. cash = get_cash_info(qt, acct); break; except: # If the initial request fails then we refresh and try again. qt.refresh_access_token(); # Save our newly generated token for next time. qt.save_token_to_yaml(yaml_path=access_info); continue; # then we print the result print(positions + cash, end = '');
import json from datetime import date from qtrade import Questrade from decimal import Decimal from forex_python.converter import CurrencyRates import mysql.connector from mysql.connector import Error import config #---connect to questrade qtrade = Questrade(token_yaml='/home/ec2-user/token.yaml') qtrade.refresh_access_token(from_yaml=True) account_ids = qtrade.get_account_id() positions = [] #---/connect to questrade #----Connect to DB db = mysql.connector.connect(host=config.DATABASE_CONFIG['host'], user=config.DATABASE_CONFIG['user'], passwd=config.DATABASE_CONFIG['password'], database=config.DATABASE_CONFIG['database']) mycursor = db.cursor() #----/Connect to DB #----Get info from Questrade for account in account_ids: print(account) temp = qtrade.get_account_positions(account_id=account) positions = positions + temp #----/Get info from Questrade
from sendgrid import SendGridAPIClient from sendgrid.helpers.mail import Mail from qtrade import Questrade from twilio.rest import Client from pyeasyga import pyeasyga # Genetic algorithms import numpy as np import random import collections import json from datetime import date with open('keys.json') as f: setting_keys = json.load(f) try: qtrade = Questrade(access_code=setting_keys['access_code']) except: qtrade = Questrade(token_yaml='./access_token.yml') qtrade.refresh_access_token(from_yaml=True) portfolio_target = { 'XEC.TO': 0.05, 'XEF.TO': 0.175, 'ZAG.TO': 0.10, 'ZLB.TO': 0.25, 'ZSP.TO': 0.275, 'XMU.TO': 0.15 } yearly_withdrawal = 0.04 # 4 pecent
from qtrade import Questrade from utils import qtrade_date_range, dates_of_interest, account_activity st.title("My Questrade Dashboard") # Authenticate st.write("Welcome! Let's start by authenticating you and your account.") qtrade_token = "qtrade_token" overwrite_token = st.text_input( "Should we look somewhere other than 'qtrade_token'?") if overwrite_token: qtrade_token = overwrite_token st.write(f"Looking for a qtrade token in env variable: {qtrade_token}") try: qtrade = Questrade(access_code=os.getenv(qtrade_token)) aapl, amzn = qtrade.ticker_information(["AAPL", "AMZN"]) st.write("Authorization sucessful via qtrade_token") except: try: qtrade = Questrade(token_yaml="access_token.yml") aapl, amzn = qtrade.ticker_information(["AAPL", "AMZN"]) st.write("Authorization sucessful via access_token") except: try: qtrade.refresh_access_token( from_yaml=True, yaml_path="access_token.yml" ) # When Qtrade refreshes token, it kills the old credentials aapl, amzn = qtrade.ticker_information(["AAPL", "AMZN"]) st.write("Authorization sucessful via refreshed access_token") except:
class QuestradeBot: def __init__(self, acctNum): # Initialize Questrade Instance if path.exists("./access_token.yml"): #print("first try in questrade") self.qtrade = Questrade(token_yaml='./access_token.yml') try: acct_list = self.qtrade.get_account_id() #print(acctNum in acct_list) assert acctNum in acct_list except: #print("first if statement") self.qtrade.refresh_access_token(from_yaml=True) self.qtrade = Questrade(token_yaml='./access_token.yml') try: assert acctNum in self.qtrade.get_account_id() except: #print("yml file removed!") remove("./access_token.yml") # get new access code access_code = new_access_code() self.qtrade = Questrade(access_code=access_code) else: #print("no yml file exist") access_code = new_access_code() self.qtrade = Questrade(access_code=access_code) try: accts_list = self.qtrade.get_account_id() except: while not isinstance(accts_list, list): #print("in while loop") access_code = new_access_code() self.qtrade = Questrade(access_code=access_code) self.acctNum = acctNum def get_acct_id(self): return self.qtrade.get_account_id() def get_ticker_info(self, symbol: str): return self.qtrade.ticker_information(symbol) def get_acct_positions(self): return self.qtrade.get_account_positions(self.acctNum) def _get_account_activities(self): return self.qtrade.get_account_activities(self.acctNum) def get_usd_total_equity(self): balance = self.get_account_balance_summary() return balance.loc['USD', 'Total_Equity'] def get_usd_total_mv(self): balance = self.get_account_balance_summary() return balance.loc['USD', 'Market_Value'] def get_cad_total_equity(self): balance = self.get_account_balance_summary() return balance.loc['CAD', 'Total_Equity'] def get_cad_total_mv(self): balance = self.get_account_balance_summary() return balance.loc['CAD', 'Market_Value'] def get_usd_total_cost(self): positions = self.get_acct_positions() total_cost = 0 for pos in positions: curr_cost = pos['totalCost'] total_cost += curr_cost return total_cost def get_account_balance_summary(self): bal = self.qtrade.get_account_balances(self.acctNum) data = { 'Currency': [], 'Cash': [], 'Market_Value': [], 'Total_Equity': [], 'Cash (%)': [], 'Investment (%)': [] } for x in bal['perCurrencyBalances']: data['Currency'].append(x['currency']) data['Cash'].append(x['cash']) data['Market_Value'].append(x['marketValue']) data['Total_Equity'].append(x['totalEquity']) if x['totalEquity'] != 0: data['Cash (%)'].append( round(100 * x['cash'] / x['totalEquity'], 2)) data['Investment (%)'].append( round(100 * x['marketValue'] / x['totalEquity'], 2)) else: data['Cash (%)'].append(0) data['Investment (%)'].append(0) df = pd.DataFrame(data) df.set_index('Currency', inplace=True) #print(tabulate(df, headers='keys')) return df def get_investment_summary(self): # p&l position_data = { 'Symbol': [], 'Description': [], 'Currency': [], 'Quantities': [], 'Market Value': [], 'Return (%)': [], 'Portfolio (%)': [] } total_market_value = self.get_usd_total_mv() total_costs = 0 positions = self.qtrade.get_account_positions(self.acctNum) for position in positions: # handle daily execution for closeQuantity if position['openQuantity'] != 0: symbol = position['symbol'] description = self.qtrade.ticker_information( symbol)['description'] qty = position['openQuantity'] cmv = position['currentMarketValue'] currency = self.qtrade.ticker_information(symbol)['currency'] cost = position['totalCost'] change = round(100 * (cmv - cost) / cost, 2) total_costs = total_costs + cost position_data['Symbol'].append(symbol) position_data['Description'].append(description) position_data['Currency'].append(currency) position_data['Quantities'].append(qty) position_data['Market Value'].append(cmv) position_data['Return (%)'].append(change) position_data['Portfolio (%)'].append( round(100 * (cmv / total_market_value), 2)) portfolio = pd.DataFrame(position_data) portfolio.set_index('Symbol', inplace=True) #portfolio.index.name = None #print(tabulate(portfolio)) return portfolio def get_historical_dividend_income(self, period): # identify the first date for creation endDate = dt.date.today().strftime("%Y-%m-%d") startDate = dt.date.today() - dt.timedelta(days=period) dtrange = pd.date_range(startDate, endDate, freq='d') months = pd.Series(dtrange.month) starts, ends = months.ne(months.shift(1)), months.ne(months.shift(-1)) startEndDates = pd.DataFrame({ 'month_starting_date': dtrange[starts].strftime('%Y-%m-%d'), 'month_ending_date': dtrange[ends].strftime('%Y-%m-%d') }) dateList = startEndDates.values.tolist() output = {} total_div_earned = 0 for date in dateList: start = date[0] end = date[1] activities = self.qtrade.get_account_activities( self.acctNum, start, end) monthly_div = 0 for activity in activities: if activity['type'] == 'Dividends': monthly_div = monthly_div + activity['netAmount'] output[dt.datetime.strptime( start, "%Y-%m-%d").strftime("%Y-%m")] = monthly_div total_div_earned = total_div_earned + monthly_div monthly_div_df = pd.DataFrame.from_dict( output, orient='index', columns=['Monthly_Dividend_Income']) return monthly_div_df def _monthly_return(self, assets): monthly_prices = pd.DataFrame() for asset in assets: monthly_prices[asset] = yf.download(asset, start=dt.datetime(2018, 1, 1), end=dt.datetime.today(), interval='1mo', progress=False)['Adj Close'] monthly_returns = monthly_prices.pct_change() monthly_returns.dropna(inplace=True) return monthly_returns def _cumulative_returns(self, assets, weights): prices = pd.DataFrame() for symbol in assets: prices[symbol] = yf.download(symbol, start=dt.datetime(2018, 1, 1), end=dt.datetime.today(), interval='1mo', progress=False)['Adj Close'] prices.dropna(inplace=True) monthly_returns = prices.pct_change() monthly_returns = monthly_returns.shift(-1) monthly_returns['port'] = monthly_returns.dot(weights) cum_returns = np.exp(np.log1p(monthly_returns['port']).cumsum())[:-1] return cum_returns def _cagr(self, assets, weights): cum_ret = self._cumulative_returns(assets, weights) first_value = cum_ret[0] last_value = cum_ret[-1] years = len(cum_ret.index) / 12 cagr = (last_value / first_value)**(1 / years) - 1 return cagr def _mdd(self, assets, weights): cum_ret = self._cumulative_returns(assets, weights) previous_peaks = cum_ret.cummax() drawdown = (cum_ret - previous_peaks) / previous_peaks port_mdd = drawdown.min() return port_mdd def calculate_portfolio_performance(self): BM_assets = ['SPY', 'IEF'] BM_weights = np.array([0.6, 0.4]) BM_cagr = round(self._cagr(BM_assets, BM_weights) * 100, 2) BM_mdd = round(self._mdd(BM_assets, BM_weights) * 100, 2) investments = self.get_investment_summary() port_assets = list(investments.index) port_weights = np.array(list(investments['Portfolio (%)'] / 100)) port_cagr = round(self._cagr(port_assets, port_weights) * 100, 2) port_mdd = round(self._mdd(port_assets, port_weights) * 100, 2) stat = { 'Portfolio': ['BenchMark', 'CurrentPortfolio'], 'CAGR (%)': [BM_cagr, port_cagr], 'MDD (%)': [BM_mdd, port_mdd] } stat_df = pd.DataFrame(stat) stat_df.set_index('Portfolio', inplace=True) return stat_df def strategy_allocation(self): # cash allocation # total equity - cash = allocatable amount total_equity = self.get_usd_total_equity() total_mv = self.get_usd_total_mv() curr_cash = total_equity - total_mv print(curr_cash) target_cash = total_equity * (self.cash_rate / 100) if target_cash < curr_cash: # invest more from curr_cash invest_amount = curr_cash - target_cash print("invest more from curr_cash") else: # sell some from investment to increase curr_cash new_market_value = total_mv - (target_cash - curr_cash) print("sell some from investment to increase curr_cash")
def __init__(self, token_yaml): self.yaml_path = token_yaml self.client = Questrade(token_yaml=token_yaml) self.refresh_access_token()
def test_get_option_chain(mock_get): """This function tests the get historical data method.""" qtrade = Questrade(token_yaml="access_token.yml") option_chain_data = qtrade.get_option_chain("XYZ") assert len(option_chain_data) == 1 assert len(option_chain_data["options"]) == 1
def test_get_account_id(mock_get): """This function tests the account ID function. """ qtrade = Questrade(token_yaml='access_token.yml') acct_id = qtrade.get_account_id() assert acct_id == [123, 456]
def test_init_via_incomplete_yaml(): """This function tests when the class is initiated via incomplete yaml file. """ with pytest.raises(Exception) as e_info: _ = Questrade(token_yaml='access_token.yml') assert str(e_info.value) == 'Refresh token was not provided.'