├── run.sh ├── run_bot.py ├── LICENSE.txt ├── setup.sh ├── parameters.py ├── README.md └── helper_monkey.py /run.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | # Run crypto bot script 4 | 5 | 6 | clear 7 | 8 | echo "=======================================================" 9 | echo "============= Running Crypto Trading Bot ==============" 10 | echo "=======================================================" 11 | 12 | source crypto/bin/activate 13 | 14 | python3 run_bot.py 15 | 16 | exit 0 17 | -------------------------------------------------------------------------------- /run_bot.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | """ 3 | Created on Sat Jan 9 09:35:49 2021 4 | 5 | @author: metalcorebear 6 | """ 7 | 8 | import parameters 9 | import helper_monkey as mojo 10 | 11 | if __name__ == '__main__': 12 | mojo.main(parameters.API_KEY, pair=parameters.pair, granularity=parameters.granularity, duration=parameters.duration, cash_buffer=parameters.cash_buffer, reframe_threshold=parameters.reframe_threshold, continuous=parameters.continuous, chandelier=parameters.chandelier) -------------------------------------------------------------------------------- /LICENSE.txt: -------------------------------------------------------------------------------- 1 | Copyright (c) 2021 Mark M. Bailey 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining a copy 4 | of this software and associated documentation files (the "Software"), to deal 5 | in the Software without restriction, including without limitation the rights 6 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 | copies of the Software, and to permit persons to whom the Software is 8 | furnished to do so, subject to the following conditions: 9 | 10 | The above copyright notice and this permission notice shall be included in all 11 | copies or substantial portions of the Software. 12 | 13 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 19 | SOFTWARE. -------------------------------------------------------------------------------- /setup.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | # Install script for crypto trading bot libraries 4 | # Created on Sat Jan 9 09:56:48 2021 5 | 6 | 7 | clear 8 | 9 | echo "=======================================================" 10 | echo "========= Instantiating Virtual Environment ===========" 11 | echo "=======================================================" 12 | 13 | read -r -p "This script will make changes to your system which may break some applications and may require you to reimage your SD card. Are you sure that you wish to continue? [y/N] " confirm 14 | 15 | if ! [[ $confirm =~ ^([yY][eE][sS]|[yY])$ ]] 16 | then 17 | exit 1 18 | fi 19 | 20 | clear 21 | echo "Installing dos2Unix" 22 | 23 | sudo apt-get install dos2unix 24 | 25 | echo "" 26 | echo "Converting files to Unix" 27 | 28 | dos2unix *.py 29 | dos2unix *.md 30 | dos2unix *.txt 31 | 32 | echo "" 33 | 34 | echo "Building virtual environment and installing Python libraries" 35 | 36 | sudo apt install python3-pip 37 | 38 | pip3 install virtualenv 39 | virtualenv crypto 40 | source crypto/bin/activate 41 | 42 | sudo apt-get install libatlas-base-dev 43 | 44 | pip3 install cbpro==1.1.4 45 | pip3 install numpy==1.16.5 46 | pip3 install pandas==0.25.1 47 | 48 | echo "All relevant Python libraries installed" 49 | 50 | chmod +x run_bot.py 51 | 52 | echo "Script is now executable" 53 | echo "First update parameters.py file" 54 | echo "Then execute ./run_bot.py" 55 | echo "You are a great American!!" 56 | 57 | exit 0 58 | -------------------------------------------------------------------------------- /parameters.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | """ 3 | Created on Fri Jan 8 11:17:23 2021 4 | 5 | @author: metalcorebear 6 | """ 7 | 8 | """ 9 | Contains all parameters for crypto trading bot. 10 | 11 | """ 12 | 13 | """ 14 | API Key parameters for Coinbase Pro. 15 | *** Ensure that all API permissions are enabled.*** 16 | 17 | # key = Public Key (str) 18 | # secret = Secret Key (str) 19 | # passphrase = Passphrase (str) 20 | 21 | """ 22 | API_KEY = {'key':'XXXXXXX', 'secret':'XXXXXXX', 'passphrase':'XXXXXXX'} 23 | 24 | 25 | """ 26 | Other bot parameters. 27 | 28 | # API_KEY = parameters.API_KEY (dict) 29 | # pair = trading pair id (str) 30 | # granularity = interval time for each iteration in seconds. Default value is 15*60 (15 minutes). Note that this has not been tested for other durations, and shorter intervals may trigger API rate limitaitons and will reduce the overall timeframe of historic OHLC data used in strategy backtesting (due to the 300 value limit of the Coinbase Pro historic data API call). (int) 31 | # duration = time to run script in seconds (int) 32 | # cash_buffer = fraction of total cash to keep in account at any given time (float) 33 | # reframe_threshold = frequency of strategy reoptimization in hours (float) 34 | # continuous - set to True if script should run indefinitely (bool) 35 | # chandelier = use/don't use chandelier method for eATR calculation (bool) 36 | 37 | """ 38 | pair = 'BTC-USD' 39 | granularity = 15*60 40 | duration = 2*60*60 41 | cash_buffer = 0.1 42 | reframe_threshold = 48.0 43 | continuous = False 44 | chandelier = False 45 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Pi Trader 2 | 3 | (C) 2021 Mark M. Bailey, PhD 4 | 5 | ## About 6 | 7 | Pi Trader is a cryptocurrency trading bot for Raspberry Pi. This script instantiates a Raspberry Pi trading bot that interfaces with the Coinbase Pro API. First, it optimizes a trading strategy by backtesting against historic data from the Coinbase exchange. The strategy is based on buy/risk parameters defined as multiples of the Exponential Average True Range (eATR). OHLC data are refreshed each iteration, and a buy/sell signal is calculated and executed if appropriate. For buy signals, the maximum possible number of coins are purchased (with a user-specified fiat buffer preserved). For sell signals, all coins are exchanged for fiat. Strategy is re-optimized at user-defined intervals.
8 | 9 | This is what I would consider to be a "dumb" trading strategy based solely on price deviations from an indicator. I am curious about exploring machine learning solutions for trading strategy optimization. Future iterations of this project may include that as an option.
10 | 11 | Note that this is an experimental bot, and like all trading strategies, I can not guarantee that it will be profitable if implemented.
12 | 13 | If anyone is interested in making this project better, I'd be happy to collaborate. 14 | 15 | ## References 16 | * Carr, Michael. "Measure Volatility With Average True Range," *Investopedia,* Nov 2019, Link: https://www.investopedia.com/articles/trading/08/average-true-range.asp#:~:text=The%20average%20true%20range%20%28ATR%29%20is%20an%20exponential,signals%2C%20while%20shorter%20timeframes%20will%20increase%20trading%20activity. 17 | * Hall, Mary. "Enter Profitable Territory With Average True Range," *Investopedia,*" Sep 2020, Link: https://www.investopedia.com/articles/trading/08/atr.asp. 18 | 19 | ## Updates 20 | * 2021-01-09: Initial commit. 21 | 22 | ## Requirements 23 | * Raspberry Pi running Debian. 24 | * Configured WiFi adaptor or ethernet connection. 25 | * Active Coinbase Pro account API with all access permissions enabled. 26 | * Recommend installing on a fresh, fully-updated image of Debian. 27 | 28 | ## Installation 29 | * In terminal, navigate to folder and run `git clone https://github.com/metalcorebear/Pi-Trader.git`. 30 | * Navigate to folder and execute `sudo ./setup.sh` to set up the virtual environment. 31 | * Edit "parameters.py" file, as appropriate, with Coinbase API keys and other parameters (see below). 32 | 33 | ## Parameters 34 | API Key parameters for Coinbase Pro.
35 | 36 | * key = Public Key (str) 37 | * secret = Secret Key (str) 38 | * passphrase = Passphrase (str)
39 | 40 | Other bot parameters:
41 | 42 | * API_KEY = parameters.API_KEY (dict) 43 | * pair = trading pair id (str) 44 | * granularity = interval time for each iteration in seconds (int) 45 | * duration = time to run script in seconds (int) 46 | * cash_buffer = fraction of total cash to keep in account at any given time (float) 47 | * reframe_threshold = frequency of strategy reoptimization in hours (float) 48 | * continuous - set to True if script should run indefinitely (bool) 49 | * chandelier = use/don't use chandelier method for eATR calculation (bool) 50 | 51 | ## Execution 52 | * To execute, simply run `./run.sh` 53 | -------------------------------------------------------------------------------- /helper_monkey.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | """ 3 | Created on Fri Jan 8 11:25:49 2021 4 | 5 | @author: metalcorebear 6 | """ 7 | 8 | import cbpro 9 | import numpy as np 10 | import pandas as pd 11 | from datetime import datetime 12 | from datetime import timedelta 13 | import time 14 | 15 | # data acquistion functions 16 | 17 | def get_product_data(pair): 18 | output = {} 19 | public_client = cbpro.PublicClient() 20 | products = public_client.get_products() 21 | for item in products: 22 | if item['id'] == pair: 23 | output.update(item) 24 | return output 25 | 26 | def get_historic_data(pair='BTC-USD', granularity=900, **options): 27 | public_client = cbpro.PublicClient() 28 | history = public_client.get_product_historic_rates(pair, granularity=granularity) 29 | history_array = np.array(history) 30 | history_pd = pd.DataFrame(history_array, columns=['time', 'low', 'high', 'open', 'close', 'volume']) 31 | return history_pd, history_array 32 | 33 | def get_latest(pair='BTC-USD', granularity=900): 34 | public_client = cbpro.PublicClient() 35 | start = datetime.now() 36 | end = start + timedelta(minutes=int(granularity/60)) 37 | history = public_client.get_product_historic_rates(pair, granularity=granularity, start=str(start.isoformat()), end=str(end.isoformat())) 38 | #history_array = np.array(history) 39 | return history 40 | 41 | def new_history(history_array, history): 42 | history_array_list = history_array.tolist() 43 | history.extend(history_array_list) 44 | history_array = np.array(history) 45 | #history = np.array(history, dtype=history_array.dtype) 46 | #history_array = np.concatenate((history, history_array), axis=0) 47 | history_array = history_array[:-1,:] 48 | return history_array 49 | 50 | 51 | # Trading functions 52 | 53 | def make_trade(pair, amount, trade_type, key, secret, passphrase): 54 | auth_client = cbpro.AuthenticatedClient(key, secret, passphrase) 55 | if trade_type == 'buy': 56 | response = auth_client.buy(order_type='market', product_id=pair, funds=amount) 57 | if trade_type == 'sell': 58 | response = auth_client.sell(order_type='market', product_id=pair, size=amount) 59 | return response 60 | 61 | def check_order_status(response, key, secret, passphrase): 62 | auth_client = cbpro.AuthenticatedClient(key, secret, passphrase) 63 | order_id = response['id'] 64 | output = True 65 | if order_id is not None: 66 | check = auth_client.get_order(order_id) 67 | output = check['settled'] 68 | return output 69 | 70 | def get_currency_balance(currency, key, secret, passphrase): 71 | auth_client = cbpro.AuthenticatedClient(key, secret, passphrase) 72 | response = auth_client.get_accounts() 73 | for item in response: 74 | if item['currency'] == currency: 75 | output = float(item['available']) 76 | return output 77 | 78 | # strategy functions 79 | 80 | def reframe_data(data): 81 | # history = [time, low, high, open, close, volume] 82 | data.rename(columns={'low':'low', 'high':'high', 'open':'Open', 'close':'Adj Close'}, inplace=True) 83 | data = data[['Open', 'high', 'low', 'Adj Close']] 84 | data = data.iloc[::-1] 85 | data = data.values 86 | data = pd.DataFrame(data, columns=['Open', 'high', 'low', 'Adj Close']) 87 | return data 88 | 89 | def eATR(data, lookback=10): 90 | m = data.values 91 | z = np.zeros((m.shape[0], 2)) 92 | m = np.concatenate((m, z), axis=1) 93 | columns = ['Open', 'high', 'low', 'Adj Close', 'ATR', 'eATR'] 94 | # calculate ATR values 95 | for i in range(1, len(m)): 96 | atr = [m[i,1] - m[i,2], abs(m[i,1] - m[i-1,3]), abs(m[i-1,3] - m[i,2])] 97 | m[i,4] = max(atr) 98 | # calcualate exponential moving average 99 | alpha = 2.0/float(lookback+1.0) 100 | sma = sum(m[:lookback,4]) / float(lookback) 101 | m[lookback,5] = sma 102 | for i in range(1,len(m)-lookback): 103 | m[i+lookback,5] = m[i+lookback,4]*alpha + m[i-1+lookback,5]*(1.0-alpha) 104 | out = pd.DataFrame(m, columns=columns, index=data.index) 105 | return out 106 | 107 | def strategize(data, strategy={'buy':1.0, 'risk':1.0}, chandelier=False): 108 | m = data.values 109 | z = np.zeros((m.shape[0], 2)) 110 | m = np.concatenate((m, z), axis=1) 111 | columns = ['Open', 'high', 'low', 'Adj Close', 'ATR', 'eATR', 'buy_point', 'sell_point'] 112 | for i in range(1, len(m)): 113 | if (m[i,3] > (m[i-1,3] + strategy['buy']*m[i-1,5])) and (m[i-1,5]>0): 114 | m[i,6] = 1 115 | if chandelier: 116 | if (m[i,3] < (m[i-1,1] - strategy['risk']*m[i-1,5])) and (m[i-1,5]>0): 117 | m[i,7] = 1 118 | else: 119 | if (m[i,3] < (m[i-1,3] - strategy['risk']*m[i-1,5])) and (m[i-1,5]>0): 120 | m[i,7] = 1 121 | out = pd.DataFrame(m, columns=columns, index=data.index) 122 | return out 123 | 124 | def evaluate(data, risk_factor=1.0, chandelier=False): 125 | m = data.values 126 | profits = [] 127 | risk_rewards = [] 128 | winning = 0 129 | all_trades = 0 130 | 131 | j = 0 132 | j_start = 1 133 | first_buy = [] 134 | 135 | for i in range(1,len(m)-1): 136 | 137 | if m[i,6] == 1: 138 | buy = m[i,3] 139 | if len(first_buy) == 0: 140 | first_buy.append(buy) 141 | if j_start < i: 142 | j_start = i+1 143 | else: 144 | j_start = j 145 | for j in range(j_start,len(m)): 146 | if m[j,7] == 1: 147 | sell = m[j,3] 148 | all_trades += 1 149 | profit = sell-buy 150 | profits.append(profit) 151 | if chandelier: 152 | stop = m[i-1,1] - risk_factor*m[i,5] 153 | else: 154 | stop = m[i-1,3] - risk_factor*m[i,5] 155 | risk_reward = profit / (buy - stop) 156 | risk_rewards.append(risk_reward) 157 | if profit > 0: 158 | winning += 1 159 | break 160 | 161 | if len(profits) != 0: 162 | expected = np.average(np.array(profits)) 163 | total_profit = sum(profits) 164 | ROI = round(total_profit / first_buy[0],2) 165 | else: 166 | expected = 0.0 167 | total_profit = 0.0 168 | ROI = 0.0 169 | total_profit = sum(profits) 170 | profits = np.array(profits) 171 | equity_curve = profits.cumsum() 172 | if all_trades != 0: 173 | hit_ratio = round(float(winning) / float(all_trades), 2) 174 | else: 175 | hit_ratio = 0.0 176 | gross_profits = [k for k in profits if k > 0] 177 | gross_losses = [abs(k) for k in profits if k < 0] 178 | if sum(gross_losses) != 0.0: 179 | profit_factor = round(sum(gross_profits) / sum(gross_losses), 2) 180 | else: 181 | profit_factor = np.nan 182 | if len(risk_rewards) != 0: 183 | risk_reward = np.average(np.array(risk_rewards)) 184 | else: 185 | risk_reward = 0.0 186 | output = {'hit_ratio':round(hit_ratio,2), 'total_trades':all_trades, 'expected':round(expected,2), 'total_profit':round(total_profit,2), 'ROI':ROI, 'profit_factor':round(profit_factor,2), 'risk_ratio':round(risk_reward,2), 'equity_curve':equity_curve} 187 | return output 188 | 189 | def simulate_strategies(data, buy_range = (1.0, 4.0, 0.25), risk_range=(1.0, 4.0, 0.25), chandelier=False): 190 | buy_i = (buy_range[1] - buy_range[0])/buy_range[2] 191 | buy_test = [buy_range[0] + float(i)*buy_range[2] for i in range(int(buy_i)+1)] 192 | risk_i = (risk_range[1] - risk_range[0])/risk_range[2] 193 | risk_test = [risk_range[0] + float(i)*risk_range[2] for i in range(int(risk_i)+1)] 194 | strategies = [] 195 | 196 | for i in buy_test: 197 | for j in risk_test: 198 | s = {'buy':i, 'risk':j} 199 | strategies.append(s) 200 | 201 | for i in range(len(strategies)): 202 | strategy = strategies[i] 203 | eatr = eATR(data) 204 | strat = strategize(eatr, strategy=strategy, chandelier=chandelier) 205 | sim = evaluate(strat, risk_factor=strategy['risk'], chandelier=chandelier) 206 | strategy.update(sim) 207 | return strategies 208 | 209 | def find_optimal_strategy(strategies): 210 | previous_number_to_beat = 0.0 211 | best_strategy = {'buy':1.0,'risk':1.0} 212 | for strategy in strategies: 213 | profit = strategy['total_profit'] 214 | if profit > previous_number_to_beat: 215 | best_strategy = strategy 216 | previous_number_to_beat = profit 217 | return best_strategy 218 | 219 | def optimize_strategy(data, buy_range = (1.0, 4.0, 0.25), risk_range=(1.0, 4.0, 0.25), chandelier=False): 220 | strategies = simulate_strategies(data, buy_range = (1.0, 4.0, 0.25), risk_range=(1.0, 4.0, 0.25), chandelier=chandelier) 221 | best_strategy = find_optimal_strategy(strategies) 222 | return best_strategy 223 | 224 | def iterate_signal(history_array, strategy, pair='BTC-USD', granularity=900, chandelier=False): 225 | history = get_latest(pair=pair, granularity=granularity) 226 | history_array = new_history(history_array, history) 227 | history_pd = pd.DataFrame(history_array, columns=['time', 'low', 'high', 'open', 'close', 'volume']) 228 | history_pd = reframe_data(history_pd) 229 | history_pd = eATR(history_pd, lookback=10) 230 | history_pd = strategize(history_pd, strategy=strategy, chandelier=chandelier) 231 | end_point = len(history_pd) - 1 232 | buy_point, sell_point = history_pd['buy_point'][end_point], history_pd['sell_point'][end_point] 233 | if buy_point == 1: 234 | buy_point = True 235 | else: 236 | buy_point = False 237 | if sell_point == 1: 238 | sell_point = True 239 | else: 240 | sell_point = False 241 | return history_array, buy_point, sell_point 242 | 243 | 244 | # MAIN FUNCTION 245 | 246 | def main(API_KEY, pair='BTC-USD', granularity=900, duration=7*24*60*60, cash_buffer=0.1, reframe_threshold=48.0, continuous=False, chandelier=False): 247 | 248 | key, secret, passphrase = API_KEY['key'], API_KEY['secret'], API_KEY['passphrase'] 249 | print('Initializing optimal trading strategy...') 250 | 251 | # Get initial optimal strategy 252 | history_pd, history_array = get_historic_data(pair=pair, granularity=granularity) 253 | reframed = reframe_data(history_pd) 254 | best_strategy = optimize_strategy(reframed, buy_range = (1.0, 4.0, 0.25), risk_range=(1.0, 4.0, 0.25), chandelier=False) 255 | 256 | # Initializing timestamp 257 | 258 | running = True 259 | total_cycles = int(duration/granularity) 260 | cycle = 0 261 | 262 | total_hours = duration/3600 263 | 264 | t = 0 265 | hour = 1.0 266 | 267 | response = {'id':None} 268 | 269 | while running: 270 | 271 | # Timecheck and reoptimization check 272 | cycle += 1 273 | if cycle >= total_cycles: 274 | if not continuous: 275 | print('{}: Duration exceeded.'.format(str(round(t)))) 276 | t += 1 277 | running = False 278 | else: 279 | running = True 280 | elapsed_hours = (cycle/total_cycles)*total_hours 281 | if (elapsed_hours - hour) >= reframe_threshold: 282 | print('{}: Reoptimizing trading strategy...'.format(str(t))) 283 | t += 1 284 | hour = hour + 1.0 285 | history_pd, history_array = get_historic_data(pair=pair, granularity=granularity) 286 | reframed = reframe_data(history_pd) 287 | best_strategy = optimize_strategy(reframed, buy_range = (1.0, 4.0, 0.25), risk_range=(1.0, 4.0, 0.25), chandelier=False) 288 | 289 | # Check crypto status 290 | output = get_product_data(pair) 291 | iteration = 0 292 | while output['status'] != 'online': 293 | print('{}: Crypto status error. Waiting...'.format(str(round(t)))) 294 | t += 1 295 | print('Waiting for {} seconds...'.format(str(granularity))) 296 | time.sleep(granularity) 297 | cycle += 1 298 | history_array, buy_point, sell_point = iterate_signal(history_array, best_strategy, pair=pair, granularity=granularity, chandelier=chandelier) 299 | output = get_product_data(pair) 300 | iteration += 1 301 | if iteration == 10: 302 | print('{}: Crypto availability timeout...try again later.'.format(str(round(t)))) 303 | t += 1 304 | running = False 305 | continue 306 | 307 | # Iterate price array 308 | history_array, buy_point, sell_point = iterate_signal(history_array, best_strategy, pair=pair, granularity=granularity, chandelier=chandelier) 309 | 310 | if sell_point: 311 | balance = get_currency_balance('BTC', key, secret, passphrase) 312 | if balance > float(output['base_min_size']): 313 | response = make_trade(pair, balance, 'sell', key, secret, passphrase) 314 | print('{}: Sold {} crypto.'.format(str(t), str(balance))) 315 | t += 1 316 | else: 317 | print('{}: No crypto to sell.'.format(str(t))) 318 | t += 1 319 | 320 | elif buy_point: 321 | balance = get_currency_balance('USD', key, secret, passphrase) 322 | if balance > 6.0: # Since the minimum buy amount is subject to change in the future this is a hotfix, and could be better solved by doing base_min_size * current_BTC_price 323 | tender = round((1.0-cash_buffer)*balance,2) 324 | response = make_trade(pair, tender, 'buy', key, secret, passphrase) 325 | print('{}: Purchased BTC for ${}.'.format(str(t), str(round(tender,2)))) 326 | t += 1 327 | else: 328 | print('{}: Not enough fiat to buy.'.format(str(t))) 329 | t += 1 330 | 331 | else: 332 | print('{}: No transaction point this iteration.'.format(str(t))) 333 | t += 1 334 | 335 | print('Waiting for {} seconds...'.format(str(granularity))) 336 | time.sleep(granularity) 337 | cycle += 1 338 | history_array, buy_point, sell_point = iterate_signal(history_array, best_strategy, pair=pair, granularity=granularity, chandelier=chandelier) 339 | 340 | # Check transaction status 341 | cleared = check_order_status(response, key, secret, passphrase) 342 | while cleared == False: 343 | print('{}: Still waiting for transaction to settle...'.format(str(t))) 344 | t += 1 345 | print('Waiting for {} seconds...'.format(str(granularity))) 346 | time.sleep(granularity) 347 | cycle += 1 348 | history_array, buy_point, sell_point = iterate_signal(history_array, best_strategy, pair=pair, granularity=granularity, chandelier=chandelier) 349 | cleared = check_order_status(response, key, secret, passphrase) 350 | continue 351 | 352 | running = True 353 | continue 354 | 355 | print('{}: Session terminated.'.format(str(t))) 356 | --------------------------------------------------------------------------------