import datetime import csv class Candles: def __init__(self, config, client): # Keep a reference to the exchange client. self.client = client # The maximum amount of data to load into memory at one time. self.max_data_loaded = config.max_data_loaded # A reference to the app configuration self.config = config # The entire loaded candle history self.candlesticks = [] # List of dictionaries of timestamped high, low, and closing values self.latest_high_values = [] self.latest_low_values = [] self.latest_close_values = [] # Values of the last candle received self.last_candle = None # List of dictionaries of timestamped volume values self.latest_vol = [] # Set the instance variable of candlesticks, latest_close_values, high, low, closing, volume, and last_candle self.set_candle_history(symbol=config.trading_pair, interval=config.chart_interval) def set_new_candle(self, cdata): self.last_candle = cdata self.latest_close_values.append({'time': cdata['time'], 'close': cdata['close']}) self.latest_high_values.append({'time': cdata['time'], 'high': cdata['high']}) self.latest_low_values.append({'time': cdata['time'], 'low': cdata['low']}) self.latest_vol.append({'time': cdata['time'], 'value': cdata['vol']}) def load_candle_history(self, symbol, interval): """ Retrieve candlestick history from a file and append it with more recent exchange data while updating the file record. This method only get called if the data is requested. This is to avoid maintaining irrelevant data files.""" start_datetime = datetime.datetime(2017, 1, 1) # Create a filename from the function parameters. # Format is symbol_interval_start_date: example - 'BTCUSDT_15m_2017-01-01' file_name = f'{symbol}_{interval}_{start_datetime.date()}' # List of price data. ,,,,, # # , candlesticks = [] try: # Populate from if it exists. print(f'Attempting to open: {file_name}') with open(file_name, 'r') as file: reader = csv.reader(file, delimiter=',') # Load the data here for row in reader: candlesticks.append(row) print('File loaded') # Open for appending file = open(file_name, 'a', newline='') candlestick_writer = csv.writer(file, delimiter=',') except IOError: # If the file doesn't exist it must be created. print(f'{file_name} not found: Creating the file') # Open for writing file = open(file_name, 'w', newline='') candlestick_writer = csv.writer(file, delimiter=',') # If no candlesticks were loaded from file. Set a date to start loading from in the # variable with a default value stored in . if not candlesticks: last_candle_stamp = start_datetime.timestamp() * 1000 else: # Set with the timestamp of the last candles on file last_candle_stamp = candlesticks[-1][0] # Request any missing candlestick data from the exchange recent_candlesticks = self.client.get_historical_klines(symbol, interval, start_str=int(last_candle_stamp)) # Discard the first row of candlestick data as it will be a duplicate***DOUBLE CHECK THIS recent_candlesticks.pop(0) # Append the candlestick list and the file for candlestick in recent_candlesticks: candlesticks.append(candlestick) candlestick_writer.writerow(candlestick) # Close the file and return the entire candlestick history file.close() return candlesticks def set_candle_history(self, symbol=None, interval=None, max_data_loaded=None): if not max_data_loaded: max_data_loaded = self.max_data_loaded if not symbol: symbol = self.config.trading_pair print(f'set_candle_history(): No symbol provided. Using{symbol}') if not interval: interval = self.config.chart_interval print(f'set_candle_history(): No interval provided. Using{interval}') if self.candlesticks: print(f'set_candle_history(): Reloading {interval} candles.') else: print(f'set_candle_history(): Loading {interval} candles.') # Load candles from file cdata = self.load_candle_history(symbol, interval) # Trim the beginning of the returned list to size of max_data_loaded of less if len(cdata) < max_data_loaded: max_data_loaded = len(cdata) self.candlesticks = cdata[-max_data_loaded:] # Set an instance dictionary of timestamped high, low, closing values self.set_latest_high_values() self.set_latest_low_values() self.set_latest_close_values() # Extract the volume data from self.candlesticks and store it in self.latest_vol self.set_latest_vol() # Set an instance reference of the last candle self.last_candle = self.convert_candle(self.candlesticks[-1]) print('set_candle_history(): Candle data Loaded') return def set_latest_vol(self): # Extracts a list of volume values from all the loaded candlestick # data and store it in a dictionary keyed to timestamp of measurement. latest_vol = [] last_clp = 0 for data in self.candlesticks: clp = int(float(data[4])) if clp < last_clp: color = 'rgba(255,82,82, 0.8)' # red else: color = 'rgba(0, 150, 136, 0.8)' # green vol_data = { "time": int(data[0]) / 1000, "value": int(float(data[5])), "color": color } last_clp = clp latest_vol.append(vol_data) self.latest_vol = latest_vol return def get_latest_vol(self, num_record=500): # Returns the latest closing values if self.latest_vol: if len(self.latest_vol) < num_record: print('Warning: get_latest_vol() - Requested too more records then available') num_record = len(self.latest_vol) return self.latest_vol[-num_record:] else: raise ValueError('Warning: get_latest_vol(): Values are not set') def set_latest_high_values(self): # Extracts a list of close values from all the loaded candlestick # data and store it in a dictionary keyed to timestamp of measurement. latest_high_values = [] for data in self.candlesticks: high_data = { "time": int(data[0]) / 1000, "high": data[2] } latest_high_values.append(high_data) self.latest_high_values = latest_high_values return def get_latest_high_values(self, num_record=500): # Returns the latest closing values if self.latest_high_values: if len(self.latest_high_values) < num_record: print('Warning: latest_high_values() - Requested too more records then available') num_record = len(self.latest_high_values) return self.latest_high_values[-num_record:] else: raise ValueError('Warning: latest_high_values(): Values are not set') def set_latest_low_values(self): # Extracts a list of close values from all the loaded candlestick # data and store it in a dictionary keyed to timestamp of measurement. latest_low_values = [] for data in self.candlesticks: low_data = { "time": int(data[0]) / 1000, "low": data[3] } latest_low_values.append(low_data) self.latest_low_values = latest_low_values return def get_latest_low_values(self, num_record=500): # Returns the latest closing values if self.latest_low_values: if len(self.latest_low_values) < num_record: print('Warning: latest_low_values() - Requested too more records then available') num_record = len(self.latest_low_values) return self.latest_low_values[-num_record:] else: raise ValueError('Warning: latest_low_values(): Values are not set') def set_latest_close_values(self): # Extracts just the timestamped close values from all the loaded candlestick # data and store it in the class instance as a dictionary. latest_close_values = [] for data in self.candlesticks: close_data = { "time": int(data[0]) / 1000, "close": data[4] } latest_close_values.append(close_data) self.latest_close_values = latest_close_values return def get_latest_close_values(self, num_record=500): # Returns the latest closing values from the class instance. if self.latest_close_values: if len(self.latest_close_values) < num_record: print('Warning: get_latest_close_values() - Requested too more records then available') num_record = len(self.latest_close_values) return self.latest_close_values[-num_record:] else: raise ValueError('Warning: get_latest_close_values(): Values are not set') @staticmethod def convert_candle(candle): # Converts the binance candle format to what lightweight charts expects. candlestick = { "time": int(candle[0]) / 1000, "open": candle[1], "high": candle[2], "low": candle[3], "close": candle[4] } return candlestick def get_candle_history(self, symbol, interval, num_records): # Returns a specified number of candle records from memory in the lightweight # charts format. if len(self.candlesticks) < num_records: print('Warning: get_candle_history() Requested more records then available') num_records = len(self.candlesticks) # Drop everything but the requested number of records candlesticks = self.candlesticks[-num_records:] # Reformat relevant candlestick data into a list of python dictionary objects. # Binance stores timestamps in milliseconds but lightweight charts doesn't, # so it gets divided by 1000 processed_candlesticks = [] for candle in candlesticks: candlestick = self.convert_candle(candle) processed_candlesticks.append(candlestick) # Return a list of candlestick objects return processed_candlesticks def get_volume(self, i_type, num_results=800): r_data = self.get_latest_vol() r_data = r_data[-num_results:] return {"type": i_type, "data": r_data}