def __validate_dispense_both_response( response_frame, template ): logger = Logger() logger.debug('Dispenser_LCDM4000: Validate response_frame to dispense_both : %s' % response_frame) template['status'] = response_frame[12] in [0x30, 0x31] template['error_cause'] = response_frame[12] template['upper_chk_requested_10'] = chr(response_frame[4]) template['upper_chk_requested_1'] = chr(response_frame[5]) template['upper_exit_requested_10'] = chr(response_frame[6]) template['upper_exit_requested_1'] = chr(response_frame[7]) template['lower_chk_requested_10'] = chr(response_frame[8]) template['lower_chk_requested_1'] = chr(response_frame[9]) template['lower_exit_requested_10'] = chr(response_frame[10]) template['lower_exit_requested_1'] = chr(response_frame[11]) template['upper_rejects_10'] = chr(response_frame[15]) template['upper_rejects_1'] = chr(response_frame[16]) template['upper_status'] = chr(response_frame[13]) template['lower_rejects_10'] = chr(response_frame[17]) template['lower_rejects_1'] = chr(response_frame[18]) template['lower_status'] = chr(response_frame[14]) return template
def command_dispense_several(self, cassets_info, cassets_dispense): logger = Logger() logger.debug('Dispenser_LCDM2000: Amount will be dispensed from several cassets') # check limits upper_count_10 = min( int(cassets_dispense[0] * 1. / 10), self.dispense_limit_both_10 ) upper_count_1 = min( cassets_dispense[0] - upper_count_10 * 10, self.dispense_limit_both_1 ) lower_count_10 = min( int(cassets_dispense[1] * 1. / 10), self.dispense_limit_both_10 ) lower_count_1 = min( cassets_dispense[1] - lower_count_10 * 10, self.dispense_limit_both_1 ) logger.debug('Dispenser_LCDM2000: upper_count_10 : %d' % upper_count_10) logger.debug('Dispenser_LCDM2000: upper_count_1 : %d' % upper_count_1) logger.debug('Dispenser_LCDM2000: lower_count_10 : %d' % lower_count_10) logger.debug('Dispenser_LCDM2000: lower_count_1 : %d' % lower_count_1) upper_count_10 = ord("%s" % upper_count_10) upper_count_1 = ord("%s" % upper_count_1) lower_count_10 = ord("%s" % lower_count_10) lower_count_1 = ord("%s" % lower_count_1) return [ 0x04,0x50,0x02,0x56,upper_count_10,upper_count_1,lower_count_10,lower_count_1,0x03, 0x04^0x50^0x02^0x56^upper_count_10^upper_count_1^lower_count_10^lower_count_1^0x03 ]
def init(engineType, user, password, host, port, db, debug=False): Db.engineType = engineType Db.user = user Db.password = password Db.port = port Db.host = host Db.db = db log = Logger() if engineType == 'sqlite': engineConnStr = engineType + ':///' + db engineConnStrLog = engineConnStr else: engineConnStr = engineType + '://' + user + ':' + password + '@' + host + ':' + str( port) + '/' + db engineConnStrLog = engineType + '://' + user + '@' + host + ':' + str( port) + '/' + db log.info('Db connection: ' + engineConnStrLog) log.debug('Db connection full: ' + engineConnStr) engine = create_engine(engineConnStr, echo=debug) session_factory = sessionmaker(bind=engine) Db.Session = scoped_session(session_factory) Db.Base = declarative_base(bind=engine) Db.metadata = Db.Base.metadata
def __validate_dispense_both_response(response_frame, template): logger = Logger() logger.debug( 'Dispenser_LCDM4000: Validate response_frame to dispense_both : %s' % response_frame) template['status'] = response_frame[12] in [0x30, 0x31] template['error_cause'] = response_frame[12] template['upper_chk_requested_10'] = chr(response_frame[4]) template['upper_chk_requested_1'] = chr(response_frame[5]) template['upper_exit_requested_10'] = chr(response_frame[6]) template['upper_exit_requested_1'] = chr(response_frame[7]) template['lower_chk_requested_10'] = chr(response_frame[8]) template['lower_chk_requested_1'] = chr(response_frame[9]) template['lower_exit_requested_10'] = chr(response_frame[10]) template['lower_exit_requested_1'] = chr(response_frame[11]) template['upper_rejects_10'] = chr(response_frame[15]) template['upper_rejects_1'] = chr(response_frame[16]) template['upper_status'] = chr(response_frame[13]) template['lower_rejects_10'] = chr(response_frame[17]) template['lower_rejects_1'] = chr(response_frame[18]) template['lower_status'] = chr(response_frame[14]) return template
def validate_status_response(self, status_frame): logger = Logger() logger.debug('Dispenser_LCDM1000: Validate status_frame %s. Len is %d' % (status_frame, len(status_frame))) # Pop ACK status_frame.pop(0) response = { 'status' : True, 'error_cause' : "%X" % status_frame[5] , 'sensor_0' : "%X" % status_frame[6], 'sensor_1' : "%X" % status_frame[7] } return response
def command_dispense_single(self, cassets_info, id_cash_box, count): logger = Logger() count_10 = min(int(count * 1. / 10), self.dispense_limit_single_10) count_1 = min(count - count_10 * 10, self.dispense_limit_single_1) logger.debug( 'Corrected counts: 10 (%d), 1 (%d)' % (count_10, count_1) ) count_10 = ord("%s" % count_10) count_1 = ord("%s" % count_1) return [ 0x04,0x50,0x02,0x45,count_10,count_1,0x03, 0x04^0x50^0x02^0x45^count_10^count_1^0x03 ]
def command_dispense_several(self, cassets_info, cassets_dispense): logger = Logger() logger.debug( 'Dispenser_LCDM4000: Amount will be dispensed from several cassettes' ) # check limits upper_count = int(cassets_dispense.get(0, 0)) m_upper_count = int(cassets_dispense.get(4, 0)) m_lower_count = int(cassets_dispense.get(5, 0)) lower_count = int(cassets_dispense.get(1, 0)) logger.debug('Dispenser_LCDM4000: upper_count : %d' % upper_count) logger.debug('Dispenser_LCDM4000: m_upper_count : %d' % m_upper_count) logger.debug('Dispenser_LCDM4000: m_lower_count : %d' % m_lower_count) logger.debug('Dispenser_LCDM4000: lower_count : %d' % lower_count) ''' The number of bills to be dispensed from cassette + 0x20 ''' upper_count += 0x20 m_upper_count += 0x20 m_lower_count += 0x20 lower_count += 0x20 return [ 0x04, 0x30, 0x02, 0x52, upper_count, m_upper_count, m_lower_count, lower_count, 0x20, 0x20, 0x20, 0x03, 0x04 ^ 0x30 ^ 0x02 ^ 0x52 ^ upper_count ^ m_upper_count ^ m_lower_count ^ lower_count ^ 0x20 ^ 0x20 ^ 0x20 ^ 0x03 ]
def validate_status_response(self, status_frame): logger = Logger() logger.debug('Dispenser_LCDM2000: Validate status_frame %s. Len is %d' % (status_frame, len(status_frame))) # Pop ACK status_frame.pop(0) response = { 'status' : True, 'error_cause' : "%X" % status_frame[5] , 'sensor_0' : "%X" % status_frame[6], 'sensor_1' : "%X" % status_frame[7] } return response
def command_dispense_several(self, cassets_info, cassets_dispense): logger = Logger() logger.debug('Dispenser_LCDM4000: Amount will be dispensed from several cassettes') # check limits upper_count = int(cassets_dispense.get(0,0)) m_upper_count = int(cassets_dispense.get(4,0)) m_lower_count = int(cassets_dispense.get(5,0)) lower_count = int(cassets_dispense.get(1,0)) logger.debug('Dispenser_LCDM4000: upper_count : %d' % upper_count) logger.debug('Dispenser_LCDM4000: m_upper_count : %d' % m_upper_count) logger.debug('Dispenser_LCDM4000: m_lower_count : %d' % m_lower_count) logger.debug('Dispenser_LCDM4000: lower_count : %d' % lower_count) ''' The number of bills to be dispensed from cassette + 0x20 ''' upper_count += 0x20 m_upper_count += 0x20 m_lower_count += 0x20 lower_count += 0x20 return [ 0x04,0x30,0x02,0x52,upper_count,m_upper_count,m_lower_count,lower_count,0x20,0x20,0x20,0x03, 0x04^0x30^0x02^0x52^upper_count^m_upper_count^m_lower_count^lower_count^0x20^0x20^0x20^0x03 ]
def command_dispense_single(self, cassets_info, id_cash_box, count): logger = Logger() count_10 = min(int(count * 1. / 10), self.dispense_limit_single_10) count_1 = min(count - count_10 * 10, self.dispense_limit_single_1) logger.debug('Corrected counts: 10 (%d), 1 (%d)' % (count_10, count_1)) count_10 = ord("%s" % count_10) count_1 = ord("%s" % count_1) return [ 0x04, 0x50, 0x02, 0x45, count_10, count_1, 0x03, 0x04 ^ 0x50 ^ 0x02 ^ 0x45 ^ count_10 ^ count_1 ^ 0x03 ]
def validate_status_response(self, status_frame): logger = Logger() logger.debug('Dispenser_LCDM4000: Validate status_frame %s. Len is %d' % (status_frame, len(status_frame))) # Pop ACK status_frame.pop(0) response = { 'status' : True, 'error_cause' : "%X" % status_frame[5], 'sensor_0' : "%X" % status_frame[6], # Upper 'sensor_1' : "%X" % status_frame[9], # Middle upper 'sensor_2' : "%X" % status_frame[12], # Middle lower 'sensor_3' : "%X" % status_frame[15], # Lower } return response
class StrategyExecutor(object): def __init__(self): self.strategies = [] self.log = Logger() def addStrategy(self, strategy): self.strategies.append(strategy) def getStrategies(self): return self.strategies def execute(self, candle): self.log.debug('Processing candle: ' + str(candle)) # TODO should catch exceptions otherwise one faulty strategy will break all the remaining ones for strategy in self.strategies: strategy.execute(candle)
def validate_status_response(self, status_frame): logger = Logger() logger.debug( 'Dispenser_LCDM4000: Validate status_frame %s. Len is %d' % (status_frame, len(status_frame))) # Pop ACK status_frame.pop(0) response = { 'status': True, 'error_cause': "%X" % status_frame[5], 'sensor_0': "%X" % status_frame[6], # Upper 'sensor_1': "%X" % status_frame[9], # Middle upper 'sensor_2': "%X" % status_frame[12], # Middle lower 'sensor_3': "%X" % status_frame[15], # Lower } return response
def command_dispense_single(self, cassets_info, id_cash_box, count): logger = Logger() logger.debug('Dispenser_LCDM4000: Request to dispense will be converted from single to multi dispense') CASSETE_ID_UPPER = 0 CASSETE_ID_LOWER = 1 CASSETE_ID_MIDDLE_UPPER = 4 CASSETE_ID_MIDDLE_LOWER = 5 cassets_dispense = { CASSETE_ID_UPPER : 0, CASSETE_ID_LOWER : 0, CASSETE_ID_MIDDLE_UPPER : 0, CASSETE_ID_MIDDLE_LOWER : 0, } cassets_dispense[id_cash_box] = count return self.command_dispense_several(cassets_info, cassets_dispense)
def __validate_dispense_single_response(response_frame, template): logger = Logger() response = {} if response_frame[3] == 0x45: logger.debug('Dispenser_LCDM4000: upper cassete validator') chk_sensor_exit_10 = 'upper_chk_requested_10' chk_sensor_exit_1 = 'upper_chk_requested_1' exit_requested_10 = 'upper_exit_requested_10' exit_requested_1 = 'upper_exit_requested_1' rejects_10 = 'upper_rejects_10' rejects_1 = 'upper_rejects_1' status = 'upper_status' elif response_frame[3] == 0x55: logger.debug('Dispenser_LCDM4000: lower cassete validator') chk_sensor_exit_10 = 'lower_chk_requested_10' chk_sensor_exit_1 = 'lower_chk_requested_1' exit_requested_10 = 'lower_exit_requested_10' exit_requested_1 = 'lower_exit_requested_1' rejects_10 = 'lower_rejects_10' rejects_1 = 'lower_rejects_1' status = 'lower_status' else: logger.debug('Dispenser_LCDM4000: UNKNOWN cassete') logger.debug('Dispenser_LCDM4000: response_frame[3] is %s' % response_frame[3]) logger.debug('Dispenser_LCDM4000: response_frame[3] is %X' % response_frame[3]) template[chk_sensor_exit_10] = chr(response_frame[4]) template[chk_sensor_exit_1] = chr(response_frame[5]) template[exit_requested_10] = chr(response_frame[6]) template[exit_requested_1] = chr(response_frame[7]) template[rejects_10] = chr(response_frame[10]) template[rejects_1] = chr(response_frame[11]) template['error_cause'] = response_frame[8] template[status] = response_frame[9] logger.debug('Dispenser_LCDM4000: Validate response_frame to dispense_single : %s' % response_frame) return template
def command_dispense_single(self, cassets_info, id_cash_box, count): logger = Logger() logger.debug( 'Dispenser_LCDM4000: Request to dispense will be converted from single to multi dispense' ) CASSETE_ID_UPPER = 0 CASSETE_ID_LOWER = 1 CASSETE_ID_MIDDLE_UPPER = 4 CASSETE_ID_MIDDLE_LOWER = 5 cassets_dispense = { CASSETE_ID_UPPER: 0, CASSETE_ID_LOWER: 0, CASSETE_ID_MIDDLE_UPPER: 0, CASSETE_ID_MIDDLE_LOWER: 0, } cassets_dispense[id_cash_box] = count return self.command_dispense_several(cassets_info, cassets_dispense)
def validate_dispense_response(self, response_frame): logger = Logger() logger.debug( 'Dispenser_LCDM1000: validate "Dispense" response frame %s' % response_frame) template = { 'status': False, 'error_cause': 0, 'upper_chk_requested_10': 0, 'upper_chk_requested_1': 0, 'upper_exit_requested_10': 0, 'upper_exit_requested_1': 0, 'upper_rejects_10': 0, 'upper_rejects_1': 0, 'upper_status': 0, 'lower_chk_requested_10': 0, 'lower_chk_requested_1': 0, 'lower_exit_requested_10': 0, 'lower_exit_requested_1': 0, 'lower_rejects_10': 0, 'lower_rejects_1': 0, 'lower_status': 0 } # Hook response_frame[3] = int(response_frame[3]) # Error while dispensing if len(response_frame) == 7: logger.debug('Dispenser_LCDM1000: len(response_frame) == 7') return False if len(response_frame) == 21: return self.__validate_dispense_both_response( response_frame, template) if response_frame[3] in [0x45, 0x55]: return self.__validate_dispense_single_response( response_frame, template)
def __validate_dispense_single_response(response_frame, template): logger = Logger() if response_frame[3] == 0x45: logger.debug('Dispenser_LCDM2000: upper cassete validator') chk_sensor_exit_10 = 'upper_chk_requested_10' chk_sensor_exit_1 = 'upper_chk_requested_1' exit_requested_10 = 'upper_exit_requested_10' exit_requested_1 = 'upper_exit_requested_1' rejects_10 = 'upper_rejects_10' rejects_1 = 'upper_rejects_1' status = 'upper_status' elif response_frame[3] == 0x55: logger.debug('Dispenser_LCDM2000: lower cassete validator') chk_sensor_exit_10 = 'lower_chk_requested_10' chk_sensor_exit_1 = 'lower_chk_requested_1' exit_requested_10 = 'lower_exit_requested_10' exit_requested_1 = 'lower_exit_requested_1' rejects_10 = 'lower_rejects_10' rejects_1 = 'lower_rejects_1' status = 'lower_status' else: logger.debug('Dispenser_LCDM2000: UNKNOWN cassete') logger.debug('Dispenser_LCDM2000: response_frame[3] is %s' % response_frame[3]) logger.debug('Dispenser_LCDM2000: response_frame[3] is %X' % response_frame[3]) template[chk_sensor_exit_10] = chr(response_frame[4]) template[chk_sensor_exit_1] = chr(response_frame[5]) template[exit_requested_10] = chr(response_frame[6]) template[exit_requested_1] = chr(response_frame[7]) template[rejects_10] = chr(response_frame[10]) template[rejects_1] = chr(response_frame[11]) template['error_cause'] = response_frame[8] template[status] = response_frame[9] logger.debug('Dispenser_LCDM2000: Validate response_frame to dispense_single : %s' % response_frame) return template
def validate_dispense_response(self, response_frame): logger = Logger() logger.debug('Dispenser_LCDM1000: validate "Dispense" response frame %s' % response_frame) template = { 'status' : False, 'error_cause' : 0, 'upper_chk_requested_10' : 0, 'upper_chk_requested_1' : 0, 'upper_exit_requested_10' : 0, 'upper_exit_requested_1' : 0, 'upper_rejects_10' : 0, 'upper_rejects_1' : 0, 'upper_status' : 0, 'lower_chk_requested_10' : 0, 'lower_chk_requested_1' : 0, 'lower_exit_requested_10' : 0, 'lower_exit_requested_1' : 0, 'lower_rejects_10' : 0, 'lower_rejects_1' : 0, 'lower_status' : 0 } # Hook response_frame[3] = int(response_frame[3]) # Error while dispensing if len(response_frame) == 7: logger.debug('Dispenser_LCDM1000: len(response_frame) == 7') return False if len(response_frame) == 21: return self.__validate_dispense_both_response(response_frame, template) if response_frame[3] in [0x45, 0x55]: return self.__validate_dispense_single_response(response_frame, template)
class Cashcode_T1500 (Thread): parity = 'N' stopbits = 1 bitesize = 8 baudrate = 9600 serial_timeout = .5 p_ack = 0x00 p_nck = 0xFF p_sync = 0x02 p_adr = 0x03 c_reset = 0x30 c_status = 0x31 c_security = 0x32 c_poll = 0x33 c_bill_types = 0x34 c_stack = 0x35 c_return = 0x36 c_ident = 0x37 c_hold = 0x38 c_barcode = 0x39 c_barcode_extract = 0x3A c_bill_table = 0x41 c_download = 0x50 c_crc32 = 0x51 c_stats = 0x60 r_power_up = 0x10 r_power_up_validator = 0x11 r_power_up_stacker = 0x12 r_initialize = 0x13 r_idling = 0x14 r_accepting = 0x15 r_stacking = 0x17 r_returning = 0x18 r_unit_disabled = 0x19 r_holding = 0x1A r_device_busy = 0x1B #generic reject codes rj_reject = 0x1C rj_drop_casset_full = 0x41 rj_drop_casset_out = 0x42 rj_validator_jammed = 0x43 rj_drop_casset_jammed = 0x44 rj_cheated = 0x45 rj_pause = 0x46 rf_failure = 0x47 #events re_escrow = 0x80 re_stacked = 0x81 re_returned = 0x82 # Errors e_insertation = 0x60 bill_types = { 2: 10, 3: 50, 4: 100, 5: 500, 6: 1000, 7: 5000 } CRC_TABLE = [ 0x0000, 0x1189, 0x2312, 0x329b, 0x4624, 0x57ad, 0x6536, 0x74bf, 0x8c48, 0x9dc1, 0xaf5a, 0xbed3, 0xca6c, 0xdbe5, 0xe97e, 0xf8f7, 0x1081, 0x0108, 0x3393, 0x221a, 0x56a5, 0x472c, 0x75b7, 0x643e, 0x9cc9, 0x8d40, 0xbfdb, 0xae52, 0xdaed, 0xcb64, 0xf9ff, 0xe876, 0x2102, 0x308b, 0x0210, 0x1399, 0x6726, 0x76af, 0x4434, 0x55bd, 0xad4a, 0xbcc3, 0x8e58, 0x9fd1, 0xeb6e, 0xfae7, 0xc87c, 0xd9f5, 0x3183, 0x200a, 0x1291, 0x0318, 0x77a7, 0x662e, 0x54b5, 0x453c, 0xbdcb, 0xac42, 0x9ed9, 0x8f50, 0xfbef, 0xea66, 0xd8fd, 0xc974, 0x4204, 0x538d, 0x6116, 0x709f, 0x0420, 0x15a9, 0x2732, 0x36bb, 0xce4c, 0xdfc5, 0xed5e, 0xfcd7, 0x8868, 0x99e1, 0xab7a, 0xbaf3, 0x5285, 0x430c, 0x7197, 0x601e, 0x14a1, 0x0528, 0x37b3, 0x263a, 0xdecd, 0xcf44, 0xfddf, 0xec56, 0x98e9, 0x8960, 0xbbfb, 0xaa72, 0x6306, 0x728f, 0x4014, 0x519d, 0x2522, 0x34ab, 0x0630, 0x17b9, 0xef4e, 0xfec7, 0xcc5c, 0xddd5, 0xa96a, 0xb8e3, 0x8a78, 0x9bf1, 0x7387, 0x620e, 0x5095, 0x411c, 0x35a3, 0x242a, 0x16b1, 0x0738, 0xffcf, 0xee46, 0xdcdd, 0xcd54, 0xb9eb, 0xa862, 0x9af9, 0x8b70, 0x8408, 0x9581, 0xa71a, 0xb693, 0xc22c, 0xd3a5, 0xe13e, 0xf0b7, 0x0840, 0x19c9, 0x2b52, 0x3adb, 0x4e64, 0x5fed, 0x6d76, 0x7cff, 0x9489, 0x8500, 0xb79b, 0xa612, 0xd2ad, 0xc324, 0xf1bf, 0xe036, 0x18c1, 0x0948, 0x3bd3, 0x2a5a, 0x5ee5, 0x4f6c, 0x7df7, 0x6c7e, 0xa50a, 0xb483, 0x8618, 0x9791, 0xe32e, 0xf2a7, 0xc03c, 0xd1b5, 0x2942, 0x38cb, 0x0a50, 0x1bd9, 0x6f66, 0x7eef, 0x4c74, 0x5dfd, 0xb58b, 0xa402, 0x9699, 0x8710, 0xf3af, 0xe226, 0xd0bd, 0xc134, 0x39c3, 0x284a, 0x1ad1, 0x0b58, 0x7fe7, 0x6e6e, 0x5cf5, 0x4d7c, 0xc60c, 0xd785, 0xe51e, 0xf497, 0x8028, 0x91a1, 0xa33a, 0xb2b3, 0x4a44, 0x5bcd, 0x6956, 0x78df, 0x0c60, 0x1de9, 0x2f72, 0x3efb, 0xd68d, 0xc704, 0xf59f, 0xe416, 0x90a9, 0x8120, 0xb3bb, 0xa232, 0x5ac5, 0x4b4c, 0x79d7, 0x685e, 0x1ce1, 0x0d68, 0x3ff3, 0x2e7a, 0xe70e, 0xf687, 0xc41c, 0xd595, 0xa12a, 0xb0a3, 0x8238, 0x93b1, 0x6b46, 0x7acf, 0x4854, 0x59dd, 0x2d62, 0x3ceb, 0x0e70, 0x1ff9, 0xf78f, 0xe606, 0xd49d, 0xc514, 0xb1ab, 0xa022, 0x92b9, 0x8330, 0x7bc7, 0x6a4e, 0x58d5, 0x495c, 0x3de3, 0x2c6a, 0x1ef1, 0x0f78 ] __prefix = '[Cashcode_t1500]' last_action = 0 time_start = 0 timeout = 0 sleep_time = .5 running = False events = [] def __init__ (self, device, baudrate = 9600, sleep_time = .5, timeout = 0): self.device, self.timeout = device, timeout self.logger = Logger() if baudrate: self.baudrate = baudrate if sleep_time > 0: self.sleep_time = sleep_time Thread.__init__(self) def connect(self): self.running = True try: self.serial = serial.serial_for_url( self.device, self.baudrate, parity = self.parity, timeout = self.serial_timeout ) self.check_status() self.logger.debug("Okay. Adapter has been connected to CASHCODE SM with data %s:%s" % (self.device, self.baudrate)) except serial.SerialException as e: message = 'Error while connecting to bill acceptor' self.logger.warning(message) raise AcceptorError(message = message, code = 500) except Exception as e: message = 'Error while connecting to bill acceptor. Error details: %s' % e self.logger.warning(message) raise AcceptorError(message = message, code = 500) self.full_reset() def disconnect(self): self.running = False try: self.logger.debug('[Cashcode_T1500]: reset') self.full_reset() time.sleep(3) self.serial.close() except: self.logger.debug('[Cashcode_T1500]: stopping with error') pass self.logger.debug('[Cashcode_T1500]: serial port has been closed') def run(self): self.logger.debug('[Cashcode_T1500]: thread has ran') self.logger.debug('[Cashcode_T1500]: timeout is %s' % self.timeout) total_summ = 0 response = None self.last_action = time.time() self.enable_bill_types() self.check_status() while self._running(self.timeout): time.sleep(self.sleep_time) # Если родительский поток не забрал все сообщения if len( self.events ): continue try: response = self.wait( wait = [self.re_stacked], skip = [self.r_idling, self.r_initialize, self.r_accepting, self.r_stacking], timeout = self.timeout ) except AcceptorError as e: message = 'Error while waiting %s. Response is %s. Error is %s' % (self.re_stacked, response, str(e.args)) self.logger.debug(message) continue try: amount = self.bill_types[response[1]] total_summ += amount self.logger.debug('Response is %s' % response) self.logger.debug('Accepted amount is %d' % amount) self.send_ack() self.events.append({'id_trx': 0, 'count': 1, 'value': amount, 'total_amount': total_summ, 'sleep_time': self.sleep_time}) except: pass self.logger.debug('[Cashcode_T1500]: run stop operations') self.disconnect() def pop_message(self): if not len(self.events): return False return self.events.pop(0) def _running(self, timeout): timestamp = int(time.time()) if not self.running: self.logger.debug("Acceptor thread stopped by flag") return False if timeout > 0 and timestamp - self.last_action > timeout: self.logger.debug("Acceptor thread stopped by timeout") #print "timestamp: %s, last_action: %s, timeout: %s" % (timestamp, self.last_action, timeout) return False return True def __calc_crc(self, data): crc = 0; for i in data: crc = self.CRC_TABLE[ (crc ^ i) & 0xFF] ^ (crc >> 8); return crc; def _read_nbytes(self, length): return str2bytes(self.serial.read(length)) def _read_byte(self): return ord(self.serial.read(1)) #reads next 2 bytes (crc length) def _read_crc(self): return self._read_nbytes(2) #reads response packey, verify crc, check header(sync, adr) def _read_response(self): header = self._read_nbytes(3) try: message = 'Header error. Is Cashcode connected to %s? Header is %s' % (self.device, header) if (header[0] <> self.p_sync or header[1] <> self.p_adr): self.logger.debug( message ) raise AcceptorError( message = message, code = 500 ) except IndexError: message = 'Header error. Header is %s' % ( header ) self.logger.debug( message ) raise AcceptorError( message = message, code = 500 ) packet = self._read_nbytes( header[2] - 3 - 2 ) crc = self._read_crc() checkCrc = int2bytes( self.__calc_crc(header + packet) ) if crc != checkCrc: self.logger.debug('%s: _read_response. Crc not satisfied their=%s , my=%s' % (self.__prefix, crc, checkCrc)) # self.logger.debug('%s: _read_response. response(len=%d) dec=%s, hex=%s ' % (self.__prefix, len(packet), packet, ints2hex(packet) )) self.validate_statement(packet) return packet def free(self, t = 0.02): time.sleep(t) ''' ******* SEND COMMANDS ************ ''' def send_single_command(self, cmd, read_response = True): packet = [self.p_sync, self.p_adr, 0x06, cmd] packet = packet + int2bytes( self.__calc_crc(packet) ) self.serial.write( serial.to_bytes( packet ) ) if read_response: return self._read_response() return True def send_command(self, data, read_response = True): packet = [ self.p_sync, self.p_adr, 5 + len(data)] + data packet = packet+ int2bytes(self.__calc_crc(packet)) self.serial.write(serial.to_bytes(packet) ) if read_response: return self._read_response() return True def status(self): return self.check_status() ''' ******* EXACT COMMANDS ************ ''' def check_status(self): status_response = self.send_single_command( self.c_status, True ) self.logger.debug( '%s: check_status: Status is "%s"' % (self.__prefix, status_response)) self.send_ack() return status_response def poll(self): return self.send_single_command( self.c_poll ) def full_reset(self): self.logger.debug( '%s: full_reset' % (self.__prefix) ) return self.__is_ack( self.send_single_command(self.c_reset) ) ''' ******** ALIASES ************** ''' def init_bill_types(self, b1, b2, b3, b4, b5, b6): init_response = self.send_command([self.c_bill_types, b3, b2, b1, b6, b5, b4]) self.logger.debug( '%s: init_bill_types: Result is "%s"' % (self.__prefix, init_response)) self.free() self.logger.debug( '%s: init_bill_types: poll' % (self.__prefix)) self.poll() self.free() self.send_ack() self.wait( wait = [self.r_idling], skip = [self.r_initialize] ) return True def enable_bill_types(self): self.logger.debug('[Cashcode_t1500->enable_bill_types]: Enable bill types') bills = 0 for i in range(2,8): bills = bills | 1 << i return self.init_bill_types(bills,0,0,0,0,0) def disable_bill_types(self): return self.init_bills(0,0,0,0,0,0) def receive_start(self, timeout = 10): self.logger.debug('%s: Starting receive of banknotes.' % self.__prefix) self.last_action = time.time() if( not self.enable_bill_types() ): raise Exception('[Cashcode_t1500->receive_start]: Bill types was not setuped') while self._running( timeout ): response = self.wait( wait = [self.re_stacked], skip = [self.r_idling, self.r_initialize, self.r_accepting, self.r_stacking] ) try: amount = self.bill_types[ response[1] ] self.logger.debug('Response is %s' % response) self.logger.debug('Accepted amount is %d' % amount) self.send_ack() return amount except: pass return 0 def receive_commit(self): response = self.send_ack() self.logger.debug('%s: commit result is %s' % (self.__prefix, response) ) def receive_rollback(self): response = self.send_single_command(self.c_return, read_response = True) self.logger.debug('%s: eject result is %s' % (self.__prefix, response) ) def send_ack(self): return self.send_single_command(self.p_ack, False) def send_nck(self): return self.send_single_command(self.p_nck, False) def bill_table(self): response = self.send_single_command(self.c_bill_table) for i in range(0,23): bytes=response[i*5 : i*5+5] print 'bytes: %s, %s' % (bytes, byte2str(bytes[1:4])) def __is_ack(self, data): return len(data)==1 and data[0]==self.p_ack def __is_nck(self, data): return len(data)==1 and data[0]==self.p_nck def _stacked_handler(self): pass def _rejected_handler(self): pass def wait(self, wait=[], skip=[], timeout = 10): self.logger.debug('[Cashcode_t1500->wait] start waiting %s' % timeout) while self._running( timeout ): response = self.poll() if(response[0] in wait): self.logger.debug('Normal response waited') return response if(response[0] in skip): self.validate_statement( response ) continue message = "[Cashcode_t1500->wait(%s, %s, %s)] got=%s" % (wait, skip, timeout, response) self.logger.debug( message ) raise AcceptorError(message, response) self.free(.2) def validate_statement(self, packet): if packet == [self.r_power_up]: self.logger.debug('State is POWER_UP') return if packet == [self.r_accepting]: self.logger.debug('State is ACCEPTING') return if packet == [self.r_stacking]: self.logger.debug('State is STACKING') return if packet == [self.rj_reject, self.e_insertation]: self.logger.debug('State is Error. Details: [rj_reject, e_insertation]') return
class Cashcode_T1500(Thread): parity = 'N' stopbits = 1 bitesize = 8 baudrate = 9600 serial_timeout = .5 p_ack = 0x00 p_nck = 0xFF p_sync = 0x02 p_adr = 0x03 c_reset = 0x30 c_status = 0x31 c_security = 0x32 c_poll = 0x33 c_bill_types = 0x34 c_stack = 0x35 c_return = 0x36 c_ident = 0x37 c_hold = 0x38 c_barcode = 0x39 c_barcode_extract = 0x3A c_bill_table = 0x41 c_download = 0x50 c_crc32 = 0x51 c_stats = 0x60 r_power_up = 0x10 r_power_up_validator = 0x11 r_power_up_stacker = 0x12 r_initialize = 0x13 r_idling = 0x14 r_accepting = 0x15 r_stacking = 0x17 r_returning = 0x18 r_unit_disabled = 0x19 r_holding = 0x1A r_device_busy = 0x1B #generic reject codes rj_reject = 0x1C rj_drop_casset_full = 0x41 rj_drop_casset_out = 0x42 rj_validator_jammed = 0x43 rj_drop_casset_jammed = 0x44 rj_cheated = 0x45 rj_pause = 0x46 rf_failure = 0x47 #events re_escrow = 0x80 re_stacked = 0x81 re_returned = 0x82 # Errors e_insertation = 0x60 bill_types = {2: 10, 3: 50, 4: 100, 5: 500, 6: 1000, 7: 5000} CRC_TABLE = [ 0x0000, 0x1189, 0x2312, 0x329b, 0x4624, 0x57ad, 0x6536, 0x74bf, 0x8c48, 0x9dc1, 0xaf5a, 0xbed3, 0xca6c, 0xdbe5, 0xe97e, 0xf8f7, 0x1081, 0x0108, 0x3393, 0x221a, 0x56a5, 0x472c, 0x75b7, 0x643e, 0x9cc9, 0x8d40, 0xbfdb, 0xae52, 0xdaed, 0xcb64, 0xf9ff, 0xe876, 0x2102, 0x308b, 0x0210, 0x1399, 0x6726, 0x76af, 0x4434, 0x55bd, 0xad4a, 0xbcc3, 0x8e58, 0x9fd1, 0xeb6e, 0xfae7, 0xc87c, 0xd9f5, 0x3183, 0x200a, 0x1291, 0x0318, 0x77a7, 0x662e, 0x54b5, 0x453c, 0xbdcb, 0xac42, 0x9ed9, 0x8f50, 0xfbef, 0xea66, 0xd8fd, 0xc974, 0x4204, 0x538d, 0x6116, 0x709f, 0x0420, 0x15a9, 0x2732, 0x36bb, 0xce4c, 0xdfc5, 0xed5e, 0xfcd7, 0x8868, 0x99e1, 0xab7a, 0xbaf3, 0x5285, 0x430c, 0x7197, 0x601e, 0x14a1, 0x0528, 0x37b3, 0x263a, 0xdecd, 0xcf44, 0xfddf, 0xec56, 0x98e9, 0x8960, 0xbbfb, 0xaa72, 0x6306, 0x728f, 0x4014, 0x519d, 0x2522, 0x34ab, 0x0630, 0x17b9, 0xef4e, 0xfec7, 0xcc5c, 0xddd5, 0xa96a, 0xb8e3, 0x8a78, 0x9bf1, 0x7387, 0x620e, 0x5095, 0x411c, 0x35a3, 0x242a, 0x16b1, 0x0738, 0xffcf, 0xee46, 0xdcdd, 0xcd54, 0xb9eb, 0xa862, 0x9af9, 0x8b70, 0x8408, 0x9581, 0xa71a, 0xb693, 0xc22c, 0xd3a5, 0xe13e, 0xf0b7, 0x0840, 0x19c9, 0x2b52, 0x3adb, 0x4e64, 0x5fed, 0x6d76, 0x7cff, 0x9489, 0x8500, 0xb79b, 0xa612, 0xd2ad, 0xc324, 0xf1bf, 0xe036, 0x18c1, 0x0948, 0x3bd3, 0x2a5a, 0x5ee5, 0x4f6c, 0x7df7, 0x6c7e, 0xa50a, 0xb483, 0x8618, 0x9791, 0xe32e, 0xf2a7, 0xc03c, 0xd1b5, 0x2942, 0x38cb, 0x0a50, 0x1bd9, 0x6f66, 0x7eef, 0x4c74, 0x5dfd, 0xb58b, 0xa402, 0x9699, 0x8710, 0xf3af, 0xe226, 0xd0bd, 0xc134, 0x39c3, 0x284a, 0x1ad1, 0x0b58, 0x7fe7, 0x6e6e, 0x5cf5, 0x4d7c, 0xc60c, 0xd785, 0xe51e, 0xf497, 0x8028, 0x91a1, 0xa33a, 0xb2b3, 0x4a44, 0x5bcd, 0x6956, 0x78df, 0x0c60, 0x1de9, 0x2f72, 0x3efb, 0xd68d, 0xc704, 0xf59f, 0xe416, 0x90a9, 0x8120, 0xb3bb, 0xa232, 0x5ac5, 0x4b4c, 0x79d7, 0x685e, 0x1ce1, 0x0d68, 0x3ff3, 0x2e7a, 0xe70e, 0xf687, 0xc41c, 0xd595, 0xa12a, 0xb0a3, 0x8238, 0x93b1, 0x6b46, 0x7acf, 0x4854, 0x59dd, 0x2d62, 0x3ceb, 0x0e70, 0x1ff9, 0xf78f, 0xe606, 0xd49d, 0xc514, 0xb1ab, 0xa022, 0x92b9, 0x8330, 0x7bc7, 0x6a4e, 0x58d5, 0x495c, 0x3de3, 0x2c6a, 0x1ef1, 0x0f78 ] __prefix = '[Cashcode_t1500]' last_action = 0 time_start = 0 timeout = 0 sleep_time = .5 running = False events = [] def __init__(self, device, baudrate=9600, sleep_time=.5, timeout=0): self.device, self.timeout = device, timeout self.logger = Logger() if baudrate: self.baudrate = baudrate if sleep_time > 0: self.sleep_time = sleep_time Thread.__init__(self) def connect(self): self.running = True try: self.serial = serial.serial_for_url(self.device, self.baudrate, parity=self.parity, timeout=self.serial_timeout) self.check_status() self.logger.debug( "Okay. Adapter has been connected to CASHCODE SM with data %s:%s" % (self.device, self.baudrate)) except serial.SerialException as e: message = 'Error while connecting to bill acceptor' self.logger.warning(message) raise AcceptorError(message=message, code=500) except Exception as e: message = 'Error while connecting to bill acceptor. Error details: %s' % e self.logger.warning(message) raise AcceptorError(message=message, code=500) self.full_reset() def disconnect(self): self.running = False try: self.logger.debug('[Cashcode_T1500]: reset') self.full_reset() time.sleep(3) self.serial.close() except: self.logger.debug('[Cashcode_T1500]: stopping with error') pass self.logger.debug('[Cashcode_T1500]: serial port has been closed') def run(self): self.logger.debug('[Cashcode_T1500]: thread has ran') self.logger.debug('[Cashcode_T1500]: timeout is %s' % self.timeout) total_summ = 0 response = None self.last_action = time.time() self.enable_bill_types() self.check_status() while self._running(self.timeout): time.sleep(self.sleep_time) # Если родительский поток не забрал все сообщения if len(self.events): continue try: response = self.wait(wait=[self.re_stacked], skip=[ self.r_idling, self.r_initialize, self.r_accepting, self.r_stacking ], timeout=self.timeout) except AcceptorError as e: message = 'Error while waiting %s. Response is %s. Error is %s' % ( self.re_stacked, response, str(e.args)) self.logger.debug(message) continue try: amount = self.bill_types[response[1]] total_summ += amount self.logger.debug('Response is %s' % response) self.logger.debug('Accepted amount is %d' % amount) self.send_ack() self.events.append({ 'id_trx': 0, 'count': 1, 'value': amount, 'total_amount': total_summ, 'sleep_time': self.sleep_time }) except: pass self.logger.debug('[Cashcode_T1500]: run stop operations') self.disconnect() def pop_message(self): if not len(self.events): return False return self.events.pop(0) def _running(self, timeout): timestamp = int(time.time()) if not self.running: self.logger.debug("Acceptor thread stopped by flag") return False if timeout > 0 and timestamp - self.last_action > timeout: self.logger.debug("Acceptor thread stopped by timeout") #print "timestamp: %s, last_action: %s, timeout: %s" % (timestamp, self.last_action, timeout) return False return True def __calc_crc(self, data): crc = 0 for i in data: crc = self.CRC_TABLE[(crc ^ i) & 0xFF] ^ (crc >> 8) return crc def _read_nbytes(self, length): return str2bytes(self.serial.read(length)) def _read_byte(self): return ord(self.serial.read(1)) #reads next 2 bytes (crc length) def _read_crc(self): return self._read_nbytes(2) #reads response packey, verify crc, check header(sync, adr) def _read_response(self): header = self._read_nbytes(3) try: message = 'Header error. Is Cashcode connected to %s? Header is %s' % ( self.device, header) if (header[0] <> self.p_sync or header[1] <> self.p_adr): self.logger.debug(message) raise AcceptorError(message=message, code=500) except IndexError: message = 'Header error. Header is %s' % (header) self.logger.debug(message) raise AcceptorError(message=message, code=500) packet = self._read_nbytes(header[2] - 3 - 2) crc = self._read_crc() checkCrc = int2bytes(self.__calc_crc(header + packet)) if crc != checkCrc: self.logger.debug( '%s: _read_response. Crc not satisfied their=%s , my=%s' % (self.__prefix, crc, checkCrc)) # self.logger.debug('%s: _read_response. response(len=%d) dec=%s, hex=%s ' % (self.__prefix, len(packet), packet, ints2hex(packet) )) self.validate_statement(packet) return packet def free(self, t=0.02): time.sleep(t) ''' ******* SEND COMMANDS ************ ''' def send_single_command(self, cmd, read_response=True): packet = [self.p_sync, self.p_adr, 0x06, cmd] packet = packet + int2bytes(self.__calc_crc(packet)) self.serial.write(serial.to_bytes(packet)) if read_response: return self._read_response() return True def send_command(self, data, read_response=True): packet = [self.p_sync, self.p_adr, 5 + len(data)] + data packet = packet + int2bytes(self.__calc_crc(packet)) self.serial.write(serial.to_bytes(packet)) if read_response: return self._read_response() return True def status(self): return self.check_status() ''' ******* EXACT COMMANDS ************ ''' def check_status(self): status_response = self.send_single_command(self.c_status, True) self.logger.debug('%s: check_status: Status is "%s"' % (self.__prefix, status_response)) self.send_ack() return status_response def poll(self): return self.send_single_command(self.c_poll) def full_reset(self): self.logger.debug('%s: full_reset' % (self.__prefix)) return self.__is_ack(self.send_single_command(self.c_reset)) ''' ******** ALIASES ************** ''' def init_bill_types(self, b1, b2, b3, b4, b5, b6): init_response = self.send_command( [self.c_bill_types, b3, b2, b1, b6, b5, b4]) self.logger.debug('%s: init_bill_types: Result is "%s"' % (self.__prefix, init_response)) self.free() self.logger.debug('%s: init_bill_types: poll' % (self.__prefix)) self.poll() self.free() self.send_ack() self.wait(wait=[self.r_idling], skip=[self.r_initialize]) return True def enable_bill_types(self): self.logger.debug( '[Cashcode_t1500->enable_bill_types]: Enable bill types') bills = 0 for i in range(2, 8): bills = bills | 1 << i return self.init_bill_types(bills, 0, 0, 0, 0, 0) def disable_bill_types(self): return self.init_bills(0, 0, 0, 0, 0, 0) def receive_start(self, timeout=10): self.logger.debug('%s: Starting receive of banknotes.' % self.__prefix) self.last_action = time.time() if (not self.enable_bill_types()): raise Exception( '[Cashcode_t1500->receive_start]: Bill types was not setuped') while self._running(timeout): response = self.wait(wait=[self.re_stacked], skip=[ self.r_idling, self.r_initialize, self.r_accepting, self.r_stacking ]) try: amount = self.bill_types[response[1]] self.logger.debug('Response is %s' % response) self.logger.debug('Accepted amount is %d' % amount) self.send_ack() return amount except: pass return 0 def receive_commit(self): response = self.send_ack() self.logger.debug('%s: commit result is %s' % (self.__prefix, response)) def receive_rollback(self): response = self.send_single_command(self.c_return, read_response=True) self.logger.debug('%s: eject result is %s' % (self.__prefix, response)) def send_ack(self): return self.send_single_command(self.p_ack, False) def send_nck(self): return self.send_single_command(self.p_nck, False) def bill_table(self): response = self.send_single_command(self.c_bill_table) for i in range(0, 23): bytes = response[i * 5:i * 5 + 5] print 'bytes: %s, %s' % (bytes, byte2str(bytes[1:4])) def __is_ack(self, data): return len(data) == 1 and data[0] == self.p_ack def __is_nck(self, data): return len(data) == 1 and data[0] == self.p_nck def _stacked_handler(self): pass def _rejected_handler(self): pass def wait(self, wait=[], skip=[], timeout=10): self.logger.debug('[Cashcode_t1500->wait] start waiting %s' % timeout) while self._running(timeout): response = self.poll() if (response[0] in wait): self.logger.debug('Normal response waited') return response if (response[0] in skip): self.validate_statement(response) continue message = "[Cashcode_t1500->wait(%s, %s, %s)] got=%s" % ( wait, skip, timeout, response) self.logger.debug(message) raise AcceptorError(message, response) self.free(.2) def validate_statement(self, packet): if packet == [self.r_power_up]: self.logger.debug('State is POWER_UP') return if packet == [self.r_accepting]: self.logger.debug('State is ACCEPTING') return if packet == [self.r_stacking]: self.logger.debug('State is STACKING') return if packet == [self.rj_reject, self.e_insertation]: self.logger.debug( 'State is Error. Details: [rj_reject, e_insertation]') return
def validate_dispense_response(self, response_frame): logger = Logger() logger.debug('Dispenser_LCDM4000: validate "Dispense" response frame %s' % (["0x%02X" % x for x in response_frame])) template = { 'status' : False, 'error_cause' : response_frame[4], 'miss' : response_frame[5], 'upper_exit_requested_1' : response_frame[6] - 0x20, 'upper_exit_requested_10' : 0, 'upper_chk_requested_1' : response_frame[6] - 0x20, 'upper_chk_requested_10' : 0, 'upper_rejects_1' : chr(response_frame[7] - 0x20), 'upper_rejects_10' : 0, 'type_1' : chr(response_frame[8]), 'm_upper_exit_requested_1' : response_frame[9] - 0x20, 'm_upper_exit_requested_10' : 0, 'm_upper_chk_requested_1' : response_frame[9] - 0x20, 'm_upper_chk_requested_10' : 0, 'm_upper_rejects_1' : chr(response_frame[10] - 0x20), 'm_upper_rejects_10' : 0, 'type_2' : chr(response_frame[11]), 'm_lower_exit_requested_1' : response_frame[12] - 0x20, 'm_lower_exit_requested_10' : 0, 'm_lower_chk_requested_1' : response_frame[12] - 0x20, 'm_lower_chk_requested_10' : 0, 'm_lower_rejects_1' : chr(response_frame[13] - 0x20), 'm_lower_rejects_10' : 0, 'type_3' : chr(response_frame[14]), 'lower_exit_requested_1' : response_frame[15] - 0x20, 'lower_exit_requested_10' : 0, 'lower_chk_requested_1' : response_frame[15] - 0x20, 'lower_chk_requested_10' : 0, 'lower_rejects_1' : chr(response_frame[16] - 0x20), 'lower_rejects_10' : 0, 'type_4' : chr(response_frame[17]), 'rsv' : 0, 'etx' : 0, 'bcc' : 0 } for k,v in template.items(): logger.debug(' > %s => %s' % (k,v)) return template if len(response_frame) == 21: logger.debug('Allright. Len response_frame is 21') else: logger.debug('Allright. Len response_frame is %s. Frame is %s' % (len(response_frame), (["%02x" % x for x in response_frame]) ) ) for k, v in template.items(): logger.debug(" ]]] %s: %s" % (k, v)) return False # Error while dispensing if len(response_frame) == 7: logger.debug('Dispenser_LCDM4000: len(response_frame) == 7') return False if len(response_frame) == 21: return self.__validate_dispense_both_response(response_frame, template) if response_frame[3] in [0x45, 0x55]: return self.__validate_dispense_single_response(response_frame, template)
class DispenserAdapter: DISPENSERS = { 'PULOON-LCDM1000' : {'class' : 'Dispenser_LCDM1000', 'path' : 'services.dispenser.dispensers.puloon_lcdm1000'}, 'PULOON-LCDM2000' : {'class' : 'Dispenser_LCDM2000', 'path' : 'services.dispenser.dispensers.puloon_lcdm2000'}, 'PULOON-LCDM4000' : {'class' : 'Dispenser_LCDM4000', 'path' : 'services.dispenser.dispensers.puloon_lcdm4000'} } serial = None # Serial Object dispenser = None # Dispenser model implementation def __init__(self, dispenser_model, port = False, baudrate = False, timeout = .6, parity = False): if dispenser_model not in self.DISPENSERS: raise DispenserError('%s is not supported. See DispenserController.DISPENSERS for available dispensers.') self.logger = Logger() self.dispenser = self.__import_class( self.DISPENSERS[dispenser_model]['path'], self.DISPENSERS[dispenser_model]['class'] )() # Set data if port: self.dispenser.port = port if baudrate: self.dispenser.baudrate = baudrate if timeout: self.dispenser.timeout = timeout if parity: self.dispenser.parity = parity @staticmethod def __import_class(class_path, class_name): try: module = __import__(class_path, fromlist = '*') return getattr(module, class_name) except Exception as e: message = "DispenserAdapter: Couldnt load class %s from path %s With error: %s" % (class_name, class_path, str(e.args)) if __debug__: print message raise ImportError(message) def connect(self): ''' Connect to serial port ''' self.logger.debug('DispenserAdapter: port is %s' % self.dispenser.port) self.logger.debug('DispenserAdapter: parity is %s' % self.dispenser.parity) self.logger.debug('DispenserAdapter: baudrate is %s' % self.dispenser.baudrate) self.logger.debug('DispenserAdapter: timeout is %s' % self.dispenser.timeout) try: self.serial = serial.serial_for_url( self.dispenser.port, self.dispenser.baudrate, parity = self.dispenser.parity, timeout = self.dispenser.timeout ) except SerialException as e: raise DispenserError('Error while connecting to dispenser', 500) try: self.status() except: pass def status(self): self.logger.debug('DispenserAdapter: get_status command received') response = self.__send( command = self.dispenser.command_status, validator = self.dispenser.validate_status_response ) self.__write( [self.dispenser.p_ack] ) if isinstance(self.dispenser, Dispenser_LCDM4000): ''' Read the EOT for LCDM4000 ''' try: self.__read( timeout = .6 ) except Exception as e: self.logger.debug('DispenserAdapter::status [forr Puloon_LCDM4000] error catched. [%s]' % str(e.args)) if not response: return False if 'status' in response: self.logger.debug('DispenserAdapter: [CMD STATUS] result: %s' % response['status']) self.logger.debug('DispenserAdapter: [CMD STATUS] info : %s' % response) return response['status'] return False def init(self): self.logger.debug('DispenserAdapter: init command received') response = self.__send( command = self.dispenser.command_init, validator = self.dispenser.validate_init_response ) self.__write( [self.dispenser.p_ack] ) if isinstance(self.dispenser, Dispenser_LCDM4000): ''' Read the EOT for LCDM4000 ''' time.sleep(2.1) # try: # self.__read( timeout = .7 ) # except Exception as e: # self.logger.debug('DispenserAdapter::init [for Puloon_LCDM4000] error catched. [%s]' % str(e.args)) self.logger.debug('DispenserAdapter: init finished with result %s' % bool(response)) if response: return True return False def purge(self): self.logger.debug('DispenserAdapter: purge command received') response = self.__send( command = self.dispenser.command_purge, validator = self.dispenser.validate_purge_response ) self.__write( [self.dispenser.p_ack] ) if isinstance(self.dispenser, Dispenser_LCDM4000): ''' Read the EOT for LCDM4000 ''' try: self.__read( timeout = .6 ) except Exception as e: self.logger.debug('DispenserAdapter::purge [for Puloon_LCDM4000] error catched. [%s]' % str(e.args)) if response: if 'status' in response: self.logger.debug('DispenserAdapter: [CMD PURGE] result: %s' % response['status']) self.logger.debug('DispenserAdapter: [CMD PURGE] info : %s' % response) return response['status'] return False def dispense(self, amount, cassets_info): ''' Dispense the bills amount: amount to dispense cassets_info: Cassets infrormation in format {id_cassete : {'command' : 0x45, 'denomination' : 0, 'charging' : 100, 'position' : 'upper'},...} ''' dispense_info = self.dispenser.get_dispense( amount, cassets_info ) command = None self.logger.debug('DispenserAdapter: Starting dispense of amount %s' % amount) # Amount is not dispensable if dispense_info['to_dispense'] == amount: raise DispenserError('Amount is not dispensable', 500) if len(dispense_info['cassets']) > 1: command = self.dispenser.command_dispense_several( cassets_info, dispense_info['cassets'] ) else: id_cassete, banknotes_count = dispense_info['cassets'].popitem() command = self.dispenser.command_dispense_single( cassets_info, id_cassete, banknotes_count ) response = self.__send( command = command, validator = self.dispenser.validate_dispense_response ) if not response: raise DispenserError('Error while dispensing (response from dispenser is None)', 500) self.__write( [self.dispenser.p_ack] ) return response def __send(self, command, validator = False): if not self.serial: raise DispenserError( 'Serial port is not opened' ) self.logger.debug('DispenserAdapter: send command in hex %s' % (["%02X" % x for x in command])) ''' Пишем, спим полсекундочки и читаем ''' self.logger.debug( '>> serial.write %s' % (["%02X" % x for x in command]) ) bytes_written = self.__write( command ) self.logger.debug( '>> serial.read' ) bytes_recd = self.__read( timeout = 1) self.logger.debug( '>> serial.read done' ) if bytes_recd == [self.dispenser.p_nck]: raise DispenserError('DispenserAdapter.__send: bytes_recd == NCK') if bytes_recd == [self.dispenser.p_ack]: self.logger.debug('DispenserAdapter.__send: bytes_recd == ACK') bytes_recd = self.__read( timeout = 60) readed = (["%02X" % x for x in bytes_recd]) else: if isinstance(self.dispenser, Dispenser_LCDM4000): self.logger.debug( "Dispenser instance is Dispenser_LCDM4000. F**k this!" ) if validator: return validator(bytes_recd) readed = (["%02X" % x for x in bytes_recd]) self.logger.debug('DispenserAdapter.__send [read from dispenser]: HEX is %s' % readed) raise DispenserError('Shit hap in HEX: %s' % readed) self.logger.debug('>> serial.read in HEX: %s' % readed) # if isinstance(self.dispenser, Dispenser_LCDM4000): # ''' Read the EOT for LCDM4000 ''' # try: # self.__read( timeout = .5 ) # except Exception as e: # self.logger.debug('DispenserAdapter::dispense [for Puloon_LCDM4000] error catched. [%s]' % str(e.args)) if validator: return validator(bytes_recd) self.logger.debug('DispenserAdapter: No validator presents. Return invalidated response.') return bytes_recd def __write(self, bytes): return self.serial.write(serial.to_bytes(bytes)) def __read(self, timeout = 60): time_start = time.time() frame = [] data = '' byte = self.serial.read(1) data += byte try: first_byte = int(byte2hex( byte ), 16) self.logger.debug('DispenserAdapter.__read: first byte is: 0x%02X' % first_byte) if first_byte == self.dispenser.p_nck: self.logger.debug('DispenserAdapter.__read: first byte is NCK') return [self.dispenser.p_nck] if first_byte == self.dispenser.p_ack: self.logger.debug('DispenserAdapter.__read: first byte is: ACK') return [self.dispenser.p_ack] byte = self.serial.read(1) data += byte self.logger.debug('DispenserAdapter.__read: Wrong first byte in response 0x%02X' % first_byte ) self.logger.debug('DispenserAdapter.__read: Next byte in response is 0x%02X' % int(byte2hex( byte )) ) except Exception as e: self.logger.debug('DispenserAdapter.__read: %s' % str(e.args) ) pass self.logger.debug('DispenserAdapter.__read: first byte is detected') while True: if time.time() - time_start > timeout: self.logger.debug('DispenserAdapter.__read: timeout to read. ') for _, i in enumerate(serial.to_bytes(data)): frame.append( int(byte2hex(i), 16) ) self.logger.debug('DispenserAdapter.__read: full dump of response: %s' % (["0x%02X" % x for x in frame])) raise DispenserError(message = 'Timeout to read', code = 500) try: if int(byte2hex(byte), 16) == self.dispenser.p_etx: break except Exception as e: self.logger.debug('Error in read cycle: %s' % str(e.args) ) time.sleep( .01 ) byte = self.serial.read(1) data += byte self.logger.debug('DispenserAdapter.__read: cycle is complete') data += self.serial.read(1) for _, i in enumerate(serial.to_bytes(data)): frame.append( int(byte2hex(i), 16) ) self.logger.debug('DispenserAdapter.__read: full dump of response: [%s]' % ([x for x in frame])) return frame
class OrderManager(object): def __init__(self, exchangeClient, marketRulesManager, strategyManager): self.exchangeClient = exchangeClient self.marketRulesManager = marketRulesManager self.strategyManager = strategyManager self.shapeNewOrders = True self.validateOrders = True self.preventImmediateLimitOrder = True self.liveOrderCache = {} self.liveTradeCache = {} self.log = Logger() def setShapeNewOrders(self, shapeNewOrders): self.shapeNewOrders = shapeNewOrders def setValidateOrders(self, validateOrders): self.validateOrders = validateOrders def setPreventImmediateLimitOrder(self, preventImmediateLimitOrder): self.preventImmediateLimitOrder = preventImmediateLimitOrder def getLiveOrderCache(self): return self.liveOrderCache def getLiveTradeCache(self): return self.liveTradeCache def reset(self): self.liveTradeCache = {} self.liveOrderCache = {} def storeOrder(self, order): Db.Session().add(order) # flush in order to generate db maintained sequence Db.Session().flush() # update internal reference only when we got new order_id if not order.int_order_ref: order.int_order_ref = Settings.ORDER_REFERENCE_PREFIX + str(order.order_id) # track order updates in the cache. o = Order() o.setFromEntity(order) self.liveOrderCache[order.order_id] = o def _storeTrade(self, trade): Db.Session().add(trade) # flush in order to generate db maintained sequence Db.Session().flush() # track trade updates in the cache. if trade reached a final state, remove it from the cache t = orders.Trade.Trade() t.setFromEntity(trade) if not trade.trade_id in self.liveTradeCache: self.liveTradeCache[trade.trade_id] = t else: if trade.trade_state in TradeStateMapper.getDbValue([TradeState.CLOSED, TradeState.CLOSE_FAILED, TradeState.OPEN_FAILED]): self.liveTradeCache.pop(trade.trade_id, None) # once trade is removed, clean also cache of corresponding trade orders for orderKey in list(self.liveOrderCache.keys()): if self.liveOrderCache[orderKey].getTradeId() == t.getTradeId(): self.liveOrderCache.pop(orderKey) else: self.liveTradeCache[trade.trade_id] = t def closeTrade(self, trade, closeTmstmp): self.log.info('Trade ' + str(trade.trade_id) + ' CLOSED.') trade.trade_state = TradeStateMapper.getDbValue(TradeState.CLOSED) trade.close_tmstmp = closeTmstmp # notify strategy about the closed trade self.strategyManager.getStrategy(trade.strategy_exec_id).tradeClosed(trade.trade_id) self._calcTradePerf(trade) def _calcTradePerf(self, trade): orders = TradeManager.getAllOrders(tradeId = trade.trade_id, orderState = OrderStateMapper.getDbValue(OrderState.FILLED)) buyPrice = Decimal(0.0) sellPrice = Decimal(0.0) for order in orders: if order.order_side == OrderSideMapper.getDbValue(OrderSide.BUY): buyPrice += order.qty * Decimal(order.price) else: sellPrice += order.qty * Decimal(order.price) diff = sellPrice - buyPrice rules = self.marketRulesManager.getSymbolRules(trade.base_asset, trade.quote_asset) if diff < 0: color = Style.BRIGHT + Fore.LIGHTWHITE_EX + Back.RED else: color = Style.BRIGHT + Fore.LIGHTWHITE_EX + Back.GREEN self.log.info(color + 'Gain: ' + str(('{:.' + str(rules.quoteAssetPrecision) + 'f}').format(diff)) + Style.RESET_ALL) def processOrderUpdate(self, orderResponse): # update order in the database based on the update response order = TradeManager.getOrder(intOrderRef = orderResponse.getClientOrderId()) order.order_state = OrderStateMapper.getDbValue(orderResponse.getOrderState()) order.lst_upd_tmstmp = datetime.now() if orderResponse.getOrderState() == OrderState.OPENED: order.open_tmstmp = orderResponse.getOrderTmstmp() if orderResponse.getOrderState() == OrderState.FILLED: self.log.debug('Order ' + str(order.order_id) + ' FILLED.') order.filled_tmstmp = orderResponse.getOrderTmstmp() if orderResponse.getOrderState() == OrderState.CANCELED: self.log.debug('Order ' + str(order.order_id) + ' CANCELLED.') # update trade in the database based on the state of orders trade = TradeManager.getTrade(tradeId = order.trade_id) if trade.trade_type == TradeTypeMapper.getDbValue(TradeType.LONG): # open trade when first confirmed BUY is received if orderResponse.getOrderSide() == OrderSide.BUY and \ orderResponse.getOrderState() == OrderState.OPENED and \ trade.trade_state == TradeStateMapper.getDbValue(TradeState.OPEN_PENDING): trade.trade_state = TradeStateMapper.getDbValue(TradeState.OPENED) trade.open_tmstmp = orderResponse.getOrderTmstmp() # close trade when no pending order is left elif orderResponse.getOrderState() in [OrderState.CANCELED, OrderState.REJECTED, OrderState.EXPIRED]: openOrders = TradeManager.getPendOrders(trade.trade_id) if len(openOrders) == 0: self.closeTrade(trade, orderResponse.getOrderTmstmp()) # handle closing of the trade based on the defined strategy elif orderResponse.getOrderState() == OrderState.FILLED: tradeCloseType = self.strategyManager.getStrategy(trade.strategy_exec_id).getTradeCloseType() TradeCloseStrategy.evalTradeClose(tradeCloseType, trade, orderResponse, self) else: raise Exception('Short trades not supported!') self.storeOrder(order) self._storeTrade(trade) def _validateOrders(self, baseAsset, quoteAsset, orders): rules = self.marketRulesManager.getSymbolRules(baseAsset, quoteAsset) for order in orders: qty = order.getQty() if qty < rules.minQty: raise Exception('Quantity is less than minimum quantity! Qty [' + str(qty) + '], minQty [' + str(rules.minQty) + ']') if qty > rules.maxQty: raise Exception('Quantity is greater than maximum quantity! Qty [' + str(qty) + '], maxQty [' + str(rules.maxQty) + ']') if (qty - rules.minQty) % rules.minQtyDenom != 0: raise Exception('Quantity is not multiply of denomination! qty [' + str(qty) + '], minQty [' + str(rules.minQty) + '], minDenom [' + str(rules.minQtyDenom) + ']') price = order.getPrice() if price: if price < rules.minPrice: raise Exception('Price is less than minimum price! Price [' + str(price) + '], minPrice [' + str(rules.minPrice) + ']') if price > rules.maxPrice: raise Exception('Price is greater than maximum price! Price [' + str(price) + '], maxPrice [' + str(rules.maxPrice) + ']') if (price - rules.minPrice) % rules.minPriceDenom != 0: raise Exception('Price is not multiply of denomination! Price [' + str(price) + '], minPrice [' + str(rules.minPrice) + '], minDenom [' + str(rules.minPriceDenom) + ']') if price * qty < rules.minNotional: raise Exception('Quantity and price is less than minimum notional value! Qty [' + str(qty) + '], price [' + str(price) + '], minNotional [' + str(rules.minNotional) + ']') stopPrice = order.getStopPrice() if stopPrice: if stopPrice < rules.minPrice: raise Exception('Stop price is less than minimum price! Stop price [' + str(stopPrice) + '], minPrice [' + str(rules.minPrice) + ']') if stopPrice > rules.maxPrice: raise Exception('Stop price is greater than maximum price! Stop price [' + str(stopPrice) + '], maxPrice [' + str(rules.maxPrice) + ']') if (stopPrice - rules.minPrice) % rules.minPriceDenom != 0: raise Exception('Stop price is not multiply of denomination! Stop price [' + str(stopPrice) + '], minPrice [' + str(rules.minPrice) + '], minDenom [' + str(rules.minPriceDenom) + ']') if stopPrice * qty < rules.minNotional: raise Exception('Quantity and stop price is less than minimum notional value! Qty [' + str(qty) + '], stop price [' + str(stopPrice) + '], minNotional [' + str(rules.minNotional) + ']') return True def _validateImmediateLimitOrder(self, candle, orders): for order in orders: if order.getOrderSide() == OrderSide.BUY: if order.getOrderType() == OrderType.LIMIT and order.getPrice() >= candle.getClose(): raise Exception("LIMIT BUY order will be executed immediately! Order price [" + str(order.getPrice()) + "], market price [" + str(candle.getClose()) + "]") if order.getOrderType() in [OrderType.STOP_LOSS_LIMIT, OrderType.STOP_LOSS_MARKET] and order.getStopPrice() <= candle.getClose(): raise Exception("STOP LOSS BUY order will be executed immediately! Order stop price [" + str(order.getStopPrice()) + "], market price [" + str(candle.getClose()) + "]") if order.getOrderType() in [OrderType.TAKE_PROFIT_LIMIT, OrderType.TAKE_PROFIT_MARKET] and order.getStopPrice() >= candle.getClose(): raise Exception("TAKE PROFIT BUY order will be executed immediately! Order stop price [" + str(order.getStopPrice()) + "], market price [" + str(candle.getClose()) + "]") if order.getOrderSide() == OrderSide.SELL: if order.getOrderType() == OrderType.LIMIT and order.getPrice() <= candle.getClose(): raise Exception("LIMIT SELL order will be executed immediately! Order price [" + str(order.getPrice()) + "], market price [" + str(candle.getClose()) + "]") if order.getOrderType() in [OrderType.STOP_LOSS_LIMIT, OrderType.STOP_LOSS_MARKET] and order.getStopPrice() >= candle.getClose(): raise Exception("STOP LOSS SELL order will be executed immediately! Order stop price [" + str(order.getStopPrice()) + "], market price [" + str(candle.getClose()) + "]") if order.getOrderType() in [OrderType.TAKE_PROFIT_LIMIT, OrderType.TAKE_PROFIT_MARKET] and order.getStopPrice() <= candle.getClose(): raise Exception("TAKE PROFIT SELL order will be executed immediately! Order stop price [" + str(order.getStopPrice()) + "], market price [" + str(candle.getClose()) + "]") def _shapeValue(self, value, minVal, minDenom, precision): self.log.debug('Shaping value [' + str(value) + '], minVal [' + str(minVal) + '], minDenom [' + str(minDenom) + '], precision [' + str(precision) + ']') ret = Decimal(minVal) + Decimal(minDenom) * Decimal((Decimal(value) - Decimal(minVal)) / Decimal(minDenom)).quantize(Decimal('1'), rounding = ROUND_DOWN) #ret = Decimal(ret).quantize(Decimal('10') ** -precision) self.log.debug('Shaped value [' + str(ret) + ']') return ret def _shapeOrders(self, baseAsset, quoteAsset, orders): rules = self.marketRulesManager.getSymbolRules(baseAsset, quoteAsset) for order in orders: if order.getQty(): self.log.debug('Shaping quantity') order.setQty(self._shapeValue(order.getQty(), rules.minQty, rules.minQtyDenom, rules.baseAssetPrecision)) if order.getPrice(): self.log.debug('Shaping price') order.setPrice(self._shapeValue(order.getPrice(), rules.minPrice, rules.minPriceDenom, rules.quoteAssetPrecision)) if order.getStopPrice(): self.log.debug('Shaping stop price') order.setStopPrice(self._shapeValue(order.getStopPrice(), rules.minPrice, rules.minPriceDenom, rules.quoteAssetPrecision)) def openTrade(self, strategyExecId, tradeType, candle): trade = Trade() trade.strategy_exec_id = strategyExecId trade.base_asset = candle.getBaseAsset() trade.quote_asset = candle.getQuoteAsset() trade.init_tmstmp = candle.getCloseTime() trade.trade_state = TradeStateMapper.getDbValue(TradeState.OPEN_PENDING) trade.trade_type = TradeTypeMapper.getDbValue(tradeType) self._storeTrade(trade) return trade def openOrder(self, trade, candle, orders): self.log.debug('Orders to be created: ' + str(len(orders))) # adjust quantity and price such that it meets market rules if self.shapeNewOrders: self._shapeOrders(candle.getBaseAsset(), candle.getQuoteAsset(), orders) # validate orders before sending them to the exchange if self.validateOrders: self._validateOrders(candle.getBaseAsset(), candle.getQuoteAsset(), orders) # make sure that limit orders are not executed immediately if self.preventImmediateLimitOrder: self._validateImmediateLimitOrder(candle, orders) for order in orders: try: dbOrder = Orders() dbOrder.trade_id = trade.trade_id dbOrder.qty = order.getQty() dbOrder.stop_price = order.getStopPrice() if order.getOrderType() == OrderType.MARKET: dbOrder.price = candle.getClose() else: dbOrder.price = order.getPrice() dbOrder.order_side = OrderSideMapper.getDbValue(order.getOrderSide()) dbOrder.order_type = OrderTypeMapper.getDbValue(order.getOrderType()) dbOrder.order_state = OrderStateMapper.getDbValue(OrderState.OPEN_PENDING_INT) dbOrder.init_tmstmp = candle.getCloseTime() self.storeOrder(dbOrder) # save the generated order id order.setOrderId(dbOrder.order_id) # save the order state (for print) order.setOrderState(OrderState.OPEN_PENDING_INT) self.log.info('Order: ' + str(order)) except: self.log.error('Could not create order ' + str(order)) raise def sendOrders(self, cretenExecDetlId): ordersToBeSent = TradeManager.getAllOrders(cretenExecDetlId = cretenExecDetlId, orderState = OrderStateMapper.getDbValue([OrderState.OPEN_PENDING_INT, OrderState.CANCEL_PENDING_INT])) for dbOrder in ordersToBeSent: if dbOrder.order_state == OrderStateMapper.getDbValue(OrderState.OPEN_PENDING_INT): try: order = Order() order.setFromEntity(dbOrder) self.log.debug('Processing order [' + str(order) + ']') trade = TradeManager.getTrade(tradeId = dbOrder.trade_id) rules = self.marketRulesManager.getSymbolRules(trade.base_asset, trade.quote_asset) qty = ('{:.' + str(rules.baseAssetPrecision) + 'f}').format(order.getQty()) price = ('{:.' + str(rules.quoteAssetPrecision) + 'f}').format(order.getPrice()) if order.getPrice() else None stopPrice = ('{:.' + str(rules.quoteAssetPrecision) + 'f}').format(order.getStopPrice()) if order.getStopPrice() else None self.log.debug('Values to be sent: qty [' + str(qty) + '], price [' + str(price) + '], stop price [' + str(stopPrice) + ']') response = self.exchangeClient.createOrder(order.getOrderSide(), order.getOrderType(), trade.base_asset, trade.quote_asset, qty, stopPrice, price, order.getIntOrderRef()) self.log.debug('Response: ' + str(response.getRawData())) if not response.getOrderState() in [OrderState.OPENED, OrderState.FILLED]: raise Exception('Unexpected response received for order [' + str(order) + ']! Expected state [' + str([OrderState.OPENED, OrderState.FILLED]) + '], received [' + str(response.getOrderState()) + ']') if order.getOrderType() == OrderType.MARKET: dbOrder.price = response.getPrice() dbOrder.ext_order_ref = response.getExtOrderRef() dbOrder.order_state = OrderStateMapper.getDbValue(OrderState.OPEN_PENDING_EXT) self.storeOrder(dbOrder) except: self.log.error('Error while creating orders!') dbOrder.order_state = OrderStateMapper.getDbValue(OrderState.OPEN_FAILED) self.storeOrder(dbOrder) raise elif dbOrder.order_state == OrderStateMapper.getDbValue(OrderState.CANCEL_PENDING_INT): try: order = Order() order.setFromEntity(dbOrder) self.log.debug('Cancelling order [' + str(order) + ']') trade = TradeManager.getTrade(tradeId = dbOrder.trade_id) response = self.exchangeClient.cancelOrder(baseAsset = trade.base_asset, quoteAsset = trade.quote_asset, clientOrderId = order.getIntOrderRef()) self.log.debug('Response: ' + str(response.getRawData())) if not response.getOrderState() in [OrderState.OPENED]: raise Exception('Unexpected response received for order [' + str(order) + ']! Expected state [' + str([OrderState.OPENED, OrderState.FILLED]) + '], received [' + str(response.getOrderState()) + ']') dbOrder.ext_order_ref = response.getExtOrderRef() dbOrder.order_state = OrderStateMapper.getDbValue(OrderState.CANCEL_PENDING_EXT) self.storeOrder(dbOrder) except: self.log.error('Error while cancelling orders!') dbOrder.order_state = OrderStateMapper.getDbValue(OrderState.CANCEL_FAILED) self.storeOrder(dbOrder) raise else: raise Exception('Creating orders for unsupporterd order state!') # TODO def init(self): pass
class ExchangeClient(object): __metaclass__ = ABCMeta def __init__(self, exchangeConfigPath): self.log = Logger(logForceDebug=False) self.exchangeConf = None if exchangeConfigPath: self.log.info('') self.log.info("Loading exchange configuration file '" + exchangeConfigPath + "'") if not os.path.isfile(exchangeConfigPath): raise Exception( "Exchange configuration file could not be loaded. '" + os.path.realpath(exchangeConfigPath) + "' does not correspond to a valid file.") with open(os.path.realpath(exchangeConfigPath)) as myfile: self.exchangeConf = json.loads(myfile.read()) jsonschema.validate(self.exchangeConf, self.getExchangeConfigSchema()) else: self.log.debug("No exchange configuration file provided.") def updateSymbolRuleFromConfig(self, symbolRule): rules = None if self.exchangeConf and "marketRules" in self.exchangeConf: for marketRule in self.exchangeConf['marketRules']: if marketRule['baseAsset'] + marketRule[ 'quoteAsset'] == symbolRule.symbol: rules = marketRule if rules: self.log.info('Updating market rules from configuration file') try: symbolRule.baseAssetPrecision = rules['baseAssetPrecision'] self.log.debug('baseAssetPrecision updated') except KeyError: pass try: symbolRule.quoteAssetPrecision = rules['quoteAssetPrecision'] self.log.debug('quoteAssetPrecision updated') except KeyError: pass try: symbolRule.minPrice = rules['minPrice'] self.log.debug('minPrice updated') except KeyError: pass try: symbolRule.maxPrice = rules['maxPrice'] self.log.debug('maxPrice updated') except KeyError: pass try: symbolRule.minPriceDenom = rules['minPriceDenom'] self.log.debug('minPriceDenom updated') except KeyError: pass try: symbolRule.minQty = rules['minQty'] self.log.debug('minQty updated') except KeyError: pass try: symbolRule.maxQty = rules['maxQty'] self.log.debug('maxQty updated') except KeyError: pass try: symbolRule.minQtyDenom = rules['minQtyDenom'] self.log.debug('minQtyDenom updated') except KeyError: pass try: symbolRule.minNotional = rules['minNotional'] self.log.debug('minNotional updated') except KeyError: pass def getExchangeConfigSchema(self): return ExchangeConfigSchema.schema @abstractmethod def getRawClient(self): pass @abstractmethod def getCandles(self, pair, interval, limit=None, startTime=None, endTime=None): pass @abstractmethod def getExchangeInfo(self, symbols=None): pass @abstractmethod def getPortfolio(self): pass @abstractmethod def createOrder(self, orderSide, orderType, baseAsset, quoteAsset, qty, stopPrice, price, clientOrderId): pass @abstractmethod def cancelOrder(self, baseAsset, quoteAsset, clientOrderId=None, extOrderRef=None): pass
Settings.client['DB_CONNECTION']['DB'], True if args['loglevel'] == 'debugalll' else False) # must be imported after db session has been initialised from db_entities.CretenExec import CretenExec from db_entities.CretenExecDetl import CretenExecDetl from db_entities.StrategyExec import StrategyExec from strategy.StrategyPerformance import StrategyPerformance log = Logger() logging.info('') with Db.session_scope(): cretenExecs = Db.Session().query(CretenExec.creten_exec_id).order_by(desc(CretenExec.creten_exec_id)).limit(args['count']).all() log.debug('Executions to be evaluated: ' + str(cretenExecs)) strategyExecs = Db.Session().query(StrategyExec).filter(CretenExecDetl.creten_exec_id.in_([x[0] for x in cretenExecs]), CretenExecDetl.creten_exec_detl_id == StrategyExec.creten_exec_detl_id).order_by(desc(StrategyExec.strategy_exec_id)).all() f = None if args['file']: f = open('Crestat_{0}.log'.format(datetime.datetime.now().strftime('%Y%m%d%H%M%S')), 'w') t = PrettyTable() t.field_names = ['SEI', 'Trades', 'Won', 'Lost', 'Won [%]', 'Gain', 'Loss', 'Gross profit', 'Gain/Loss', 'Avg gain', 'Max gain', 'Avg loss', 'Max loss', 'Avg trade length', 'Max trade length'] if args['file']: f.write(';'.join(t.field_names) + '\n')
class Dispenser: ''' Configuration ''' port = '/dev/ttyUSB' baudrate = 9600 timeout = 1 data_bits = 8 # 8 bits stop_bits = 1 # 1 bit parity = serial.PARITY_NONE # No parity bps = 3.5 # Banknotes per second ''' Limits for dispensing ''' dispense_limit_single_10 = 6 dispense_limit_single_1 = 9 dispense_limit_both_10 = 6 dispense_limit_both_1 = 9 dispense_limit = 60 # Limit banknotes count for dispensing from 1 cassete ''' PROTOCOL > CONSTANTS ''' p_soh = 0x01 # Start of Header p_stx = 0x02 # Start of Text p_etx = 0x03 # End of Text p_eot = 0x04 # Start of Transmission p_ack = 0x06 # ACK p_nck = 0x15 # NCK p_id = 0x50 # Communication ID p_bcc = 0x00 # Block Check Character: BCC can be gotten through Exclusive-OR (XOR) from the start of each message to ETX @staticmethod def get_cassete_by_denomination(cassets, denomination): for id_cassete, cassete_info in cassets.items(): if cassete_info['denomination'] == denomination: return id_cassete return -1 def get_dispense(self, amount, cassets_info): self.logger = Logger() ''' Method calculates cash dispensing between cassets amount: Amount to dispense cassets_info: Cassets infrormation in format {id_cassete : {'command' : 0x45, 'denomination' : 0, 'charging' : 100, 'position' : 'upper'},...} return { 'to_dispense' : 5, 'cassets' : { 0 : {'amount' : 100, 'count' : 10, 'denomination' : 10}, 1 : {'amount' : 1000, 'count' : 10, 'denomination' : 100}, ... } } ''' dispense = {'to_dispense': amount, 'cassets': {}} # Dynamic cassets information cassets = ([cassete_info['denomination'] for cassete_info in cassets_info.values()]) cassets.sort() while dispense['to_dispense'] > 0 and len( cassets ): self.logger.debug('[DISPENSER]: Amount to dispense: %s' % dispense['to_dispense']) self.logger.debug('[DISPENSER]: Next cassets are available: %s' % cassets_info) # select max denomination denomination = cassets.pop() self.logger.debug( '[DISPENSER]: selected denomination: %s' % denomination ) id_cassete = self.get_cassete_by_denomination( cassets_info, denomination ) # if no available cassets if id_cassete == -1: self.logger.debug('[DISPENSER]: no more cassets are available to dispense amount %s' % dispense['to_dispense']) continue # check count of banknotes banknotes_count = min( int( dispense['to_dispense'] * 1. / denomination ), # Count of full-dispensing cassets_info[id_cassete]['charging'], # Count of available banknotes self.dispense_limit # Dispenser limit ) # If no more banknotes if not banknotes_count: self.logger.debug( '[DISPENSER]: no such banknotes in cassete #%d' % id_cassete ) continue self.logger.debug( '[DISPENSER]: from selected cassete #%d will exit %d banknotes with denomination %d' % (id_cassete, banknotes_count, denomination) ) dispense['cassets'][id_cassete] = banknotes_count dispense['to_dispense'] -= banknotes_count * denomination return dispense
def validate_dispense_response(self, response_frame): logger = Logger() logger.debug( 'Dispenser_LCDM4000: validate "Dispense" response frame %s' % (["0x%02X" % x for x in response_frame])) template = { 'status': False, 'error_cause': response_frame[4], 'miss': response_frame[5], 'upper_exit_requested_1': response_frame[6] - 0x20, 'upper_exit_requested_10': 0, 'upper_chk_requested_1': response_frame[6] - 0x20, 'upper_chk_requested_10': 0, 'upper_rejects_1': chr(response_frame[7] - 0x20), 'upper_rejects_10': 0, 'type_1': chr(response_frame[8]), 'm_upper_exit_requested_1': response_frame[9] - 0x20, 'm_upper_exit_requested_10': 0, 'm_upper_chk_requested_1': response_frame[9] - 0x20, 'm_upper_chk_requested_10': 0, 'm_upper_rejects_1': chr(response_frame[10] - 0x20), 'm_upper_rejects_10': 0, 'type_2': chr(response_frame[11]), 'm_lower_exit_requested_1': response_frame[12] - 0x20, 'm_lower_exit_requested_10': 0, 'm_lower_chk_requested_1': response_frame[12] - 0x20, 'm_lower_chk_requested_10': 0, 'm_lower_rejects_1': chr(response_frame[13] - 0x20), 'm_lower_rejects_10': 0, 'type_3': chr(response_frame[14]), 'lower_exit_requested_1': response_frame[15] - 0x20, 'lower_exit_requested_10': 0, 'lower_chk_requested_1': response_frame[15] - 0x20, 'lower_chk_requested_10': 0, 'lower_rejects_1': chr(response_frame[16] - 0x20), 'lower_rejects_10': 0, 'type_4': chr(response_frame[17]), 'rsv': 0, 'etx': 0, 'bcc': 0 } for k, v in template.items(): logger.debug(' > %s => %s' % (k, v)) return template if len(response_frame) == 21: logger.debug('Allright. Len response_frame is 21') else: logger.debug('Allright. Len response_frame is %s. Frame is %s' % (len(response_frame), (["%02x" % x for x in response_frame]))) for k, v in template.items(): logger.debug(" ]]] %s: %s" % (k, v)) return False # Error while dispensing if len(response_frame) == 7: logger.debug('Dispenser_LCDM4000: len(response_frame) == 7') return False if len(response_frame) == 21: return self.__validate_dispense_both_response( response_frame, template) if response_frame[3] in [0x45, 0x55]: return self.__validate_dispense_single_response( response_frame, template)
class MarketRulesManager(object): def __init__(self, exchangeClient): self.exchangeClient = exchangeClient self.symbolRules = {} self.commission = 0.0 self.log = Logger(logForceDebug=False) def init(self, symbols): rules = self.exchangeClient.getExchangeInfo(symbols) for rule in rules: self.log.debug('Market rules for ' + str(rule.symbol) + ':') self.log.debug('\tTrading status: ' + str(rule.status)) self.log.debug('\tQuote asset precision: ' + str(rule.quoteAssetPrecision)) self.log.debug('\tBase asset precision: ' + str(rule.baseAssetPrecision)) self.log.debug('\tOrder types: ' + str(rule.orderTypes)) self.log.debug('\tIceberg allowed: ' + str(rule.icebergAllowed)) self.log.debug('\tMin price: ' + str(rule.minPrice)) self.log.debug('\tMax price: ' + str(rule.maxPrice)) self.log.debug('\tMin price denomination: ' + str(rule.minPriceDenom)) self.log.debug('\tMin quantity: ' + str(rule.minQty)) self.log.debug('\tMax quantity: ' + str(rule.maxQty)) self.log.debug('\tMin quantity denomination: ' + str(rule.minQtyDenom)) self.log.debug('\tMin notional: ' + str(rule.minNotional)) self.symbolRules[rule.symbol] = rule def getSymbolRules(self, baseAsset, quoteAsset): return self.symbolRules[baseAsset + quoteAsset] def setCommission(self, commission): self.commission = commission def getCommission(self): return self.commission
class Dispenser: ''' Configuration ''' port = '/dev/ttyUSB' baudrate = 9600 timeout = 1 data_bits = 8 # 8 bits stop_bits = 1 # 1 bit parity = serial.PARITY_NONE # No parity bps = 3.5 # Banknotes per second ''' Limits for dispensing ''' dispense_limit_single_10 = 6 dispense_limit_single_1 = 9 dispense_limit_both_10 = 6 dispense_limit_both_1 = 9 dispense_limit = 60 # Limit banknotes count for dispensing from 1 cassete ''' PROTOCOL > CONSTANTS ''' p_soh = 0x01 # Start of Header p_stx = 0x02 # Start of Text p_etx = 0x03 # End of Text p_eot = 0x04 # Start of Transmission p_ack = 0x06 # ACK p_nck = 0x15 # NCK p_id = 0x50 # Communication ID p_bcc = 0x00 # Block Check Character: BCC can be gotten through Exclusive-OR (XOR) from the start of each message to ETX @staticmethod def get_cassete_by_denomination(cassets, denomination): for id_cassete, cassete_info in cassets.items(): if cassete_info['denomination'] == denomination: return id_cassete return -1 def get_dispense(self, amount, cassets_info): self.logger = Logger() ''' Method calculates cash dispensing between cassets amount: Amount to dispense cassets_info: Cassets infrormation in format {id_cassete : {'command' : 0x45, 'denomination' : 0, 'charging' : 100, 'position' : 'upper'},...} return { 'to_dispense' : 5, 'cassets' : { 0 : {'amount' : 100, 'count' : 10, 'denomination' : 10}, 1 : {'amount' : 1000, 'count' : 10, 'denomination' : 100}, ... } } ''' dispense = {'to_dispense': amount, 'cassets': {}} # Dynamic cassets information cassets = ([ cassete_info['denomination'] for cassete_info in cassets_info.values() ]) cassets.sort() while dispense['to_dispense'] > 0 and len(cassets): self.logger.debug('[DISPENSER]: Amount to dispense: %s' % dispense['to_dispense']) self.logger.debug('[DISPENSER]: Next cassets are available: %s' % cassets_info) # select max denomination denomination = cassets.pop() self.logger.debug('[DISPENSER]: selected denomination: %s' % denomination) id_cassete = self.get_cassete_by_denomination( cassets_info, denomination) # if no available cassets if id_cassete == -1: self.logger.debug( '[DISPENSER]: no more cassets are available to dispense amount %s' % dispense['to_dispense']) continue # check count of banknotes banknotes_count = min( int(dispense['to_dispense'] * 1. / denomination), # Count of full-dispensing cassets_info[id_cassete] ['charging'], # Count of available banknotes self.dispense_limit # Dispenser limit ) # If no more banknotes if not banknotes_count: self.logger.debug( '[DISPENSER]: no such banknotes in cassete #%d' % id_cassete) continue self.logger.debug( '[DISPENSER]: from selected cassete #%d will exit %d banknotes with denomination %d' % (id_cassete, banknotes_count, denomination)) dispense['cassets'][id_cassete] = banknotes_count dispense['to_dispense'] -= banknotes_count * denomination return dispense
Settings.client['DB_CONNECTION']['PORT'], Settings.client['DB_CONNECTION']['DB'], True if args['loglevel'] == 'debugalll' else False) # following packages must be imported only after database initilization from engines.BackTester import BackTester from engines.RealTimeSimulator import RealTimeSimulator from db_managers.ExecManager import ExecManager if not os.path.isfile(args['inputconfig']): raise Exception('Input configuration file could not be loaded. ' + os.path.realpath(args['inputconfig']) + " does not correspond to a valid file.") with open(os.path.realpath(args['inputconfig'])) as myfile: inputConf = myfile.read() with Db.session_scope(): cretenExec = ExecManager.createCretenExec(args['mode'], inputConf) cretenExecId = cretenExec.creten_exec_id log = Logger() log.debug("Creating exchange client...") client = ExchangeClientFactory.getExchangeClient(args) log.debug("Creating creten engine...") if args['mode'] == PARAM_MODE_BACKTEST: simulator = BackTester(client, inputConf, cretenExecId) simulator.run() elif args['mode'] == PARAM_MODE_REALTIMETEST: simulator = RealTimeSimulator(client, inputConf, cretenExecId) simulator.run()
class ExchangeEventSimulator(object): def __init__(self, marketDataManager, orderManager, portfolioManager, exchangeDataListener): self.marketDataManager = marketDataManager self.orderManager = orderManager self.portfolioManager = portfolioManager self.exchangeDataListener = exchangeDataListener self.cretenExecDetlId = None self.log = Logger(logPrefix='', logForceDebug=False) # Comparator to evaluate order in which trade orders are evaluated # Primary priority is order type and the priority is # 1. market orders # 2. stop loss market orders # 3. stop loss limit orders # 4. take profit market orders # 5. take profit limit orders # 6. limit orders # Secondary priority depends on order type and side and is as follows: # market => order_id # stop loss sell market => stop price desc # stop loss sell limit => stop price desc # stop loss buy market => stop price # stop loss buy limit => stop price # take profit sell market => stop price # take profit sell limit => stop price # take profit buy market => stop price desc # take profit buy limit => stop price desc # limit => price @staticmethod def orderIterComp(order): if order.getOrderType() == OrderType.MARKET: primaryPrio = 1 secondaryPrio = order.getOrderId() elif order.getOrderType( ) == OrderType.STOP_LOSS_MARKET and order.getOrderSide( ) == OrderSide.SELL: primaryPrio = 2 secondaryPrio = -1 * order.getStopPrice() elif order.getOrderType( ) == OrderType.STOP_LOSS_LIMIT and order.getOrderSide( ) == OrderSide.SELL: primaryPrio = 3 secondaryPrio = -1 * order.getStopPrice() elif order.getOrderType( ) == OrderType.STOP_LOSS_MARKET and order.getOrderSide( ) == OrderSide.BUY: primaryPrio = 4 secondaryPrio = order.getStopPrice() elif order.getOrderType( ) == OrderType.STOP_LOSS_LIMIT and order.getOrderSide( ) == OrderSide.BUY: primaryPrio = 5 secondaryPrio = order.getStopPrice() elif order.getOrderType( ) == OrderType.TAKE_PROFIT_MARKET and order.getOrderSide( ) == OrderSide.SELL: primaryPrio = 6 secondaryPrio = order.getStopPrice() elif order.getOrderType( ) == OrderType.TAKE_PROFIT_LIMIT and order.getOrderSide( ) == OrderSide.SELL: primaryPrio = 7 secondaryPrio = order.getStopPrice() elif order.getOrderType( ) == OrderType.TAKE_PROFIT_MARKET and order.getOrderSide( ) == OrderSide.BUY: primaryPrio = 8 secondaryPrio = -1 * order.getStopPrice() elif order.getOrderType( ) == OrderType.TAKE_PROFIT_LIMIT and order.getOrderSide( ) == OrderSide.BUY: primaryPrio = 9 secondaryPrio = -1 * order.getStopPrice() elif order.getOrderType() == OrderType.LIMIT: primaryPrio = 10 secondaryPrio = order.getPrice() else: raise Exception("Unknown order type for order id " + str(order.getOrderId())) return primaryPrio, secondaryPrio def simulateEvent(self, candle): # Simulate events as long as there is a trade pending confirmation (opening, cancellation, ...). Several iterations # might be required since in some cases new pending orders are produced within previous iteration while True: # Trade order cache has to be iterated via keys since its content may change on the fly (e.g. a filled trade can # lead to cancellation of pending trades). Furthermore, the keys are ordered by priority (for more details # see comparator description) for orderKey in sorted( self.orderManager.getLiveOrderCache().keys(), key=lambda x: self.orderIterComp(self.orderManager. getLiveOrderCache()[x])): # Retrieving order from the cache has to be encapsulated in a try block since some orders could # disappear during processing (e.g. when trade is closed) try: order = self.orderManager.getLiveOrderCache()[orderKey] except KeyError: break trade = self._findTrade(order.getTradeId()) # always confirm new orders if order.getOrderState() == OrderState.OPEN_PENDING_EXT: self.log.debug('>>> [' + str(candle) + ']') self.log.debug('Confirming order ' + str(order.getOrderId())) response = OrderResponse( baseAsset=trade.getBaseAsset(), quoteAsset=trade.getQuoteAsset(), orderSide=order.getOrderSide(), orderType=order.getOrderType(), origQty=order.getQty(), lastExecutedQty='0', sumExecutedQty='0', price=order.getPrice(), orderState=OrderState.OPENED, orderTmstmp=order.getInitTmstmp(), clientOrderId=order.getIntOrderRef(), extOrderRef=order.getIntOrderRef()) self.exchangeDataListener.processOrderUpdate(response) # always cancel orders pending cancellation if order.getOrderState() == OrderState.CANCEL_PENDING_EXT: self.log.debug('>>> [' + str(candle) + ']') self.log.debug('Cancelling order ' + str(order.getOrderId())) response = OrderResponse( baseAsset=trade.getBaseAsset(), quoteAsset=trade.getQuoteAsset(), orderSide=order.getOrderSide(), orderType=order.getOrderType(), origQty=order.getQty(), lastExecutedQty='0', sumExecutedQty='0', price=order.getPrice(), orderState=OrderState.CANCELED, orderTmstmp=candle.getCloseTime(), clientOrderId=order.getIntOrderRef(), extOrderRef=order.getIntOrderRef()) self.exchangeDataListener.processOrderUpdate(response) # evaluate market orders if order.getOrderState() == OrderState.OPENED and \ order.getOrderType() == OrderType.MARKET: self.log.debug('>>> [' + str(candle) + ']') self.log.debug('Filling market order ' + str(order.getOrderId())) response = OrderResponse( baseAsset=trade.getBaseAsset(), quoteAsset=trade.getQuoteAsset(), orderSide=order.getOrderSide(), orderType=order.getOrderType(), origQty=order.getQty(), lastExecutedQty=order.getQty(), sumExecutedQty=order.getQty(), price=order.getPrice(), orderState=OrderState.FILLED, orderTmstmp=order.getOpenTmstmp(), clientOrderId=order.getIntOrderRef(), extOrderRef=order.getIntOrderRef()) self.exchangeDataListener.processOrderUpdate(response) self._updatePosition(response) # evaluate limit orders if order.getOrderState() == OrderState.OPENED and \ order.getOrderType() == OrderType.LIMIT and \ ( (order.getOrderSide() == OrderSide.SELL and candle.getUpperBody() >= order.getPrice()) or (order.getOrderSide() == OrderSide.BUY and candle.getLowerBody() <= order.getPrice()) ): self.log.info('>>> [' + str(candle) + ']') self.log.info('Filling limit order ' + str(order.getOrderId())) response = OrderResponse( baseAsset=trade.getBaseAsset(), quoteAsset=trade.getQuoteAsset(), orderSide=order.getOrderSide(), orderType=order.getOrderType(), origQty=order.getQty(), lastExecutedQty=order.getQty(), sumExecutedQty=order.getQty(), price=order.getPrice(), orderState=OrderState.FILLED, orderTmstmp=candle.getCloseTime(), clientOrderId=order.getIntOrderRef(), extOrderRef=order.getIntOrderRef()) self.exchangeDataListener.processOrderUpdate(response) self._updatePosition(response) # evaluate stop loss market/limit orders if order.getOrderState() == OrderState.OPENED and \ order.getOrderType() in [OrderType.STOP_LOSS_LIMIT, OrderType.STOP_LOSS_MARKET] and \ ( (order.getOrderSide() == OrderSide.SELL and candle.getLow() <= order.getStopPrice()) or (order.getOrderSide() == OrderSide.BUY and candle.getHigh() >= order.getStopPrice()) ): self.log.info('>>> [' + str(candle) + ']') self.log.info('Filling stop loss order ' + str(order.getOrderId())) response = OrderResponse( baseAsset=trade.getBaseAsset(), quoteAsset=trade.getQuoteAsset(), orderSide=order.getOrderSide(), orderType=order.getOrderType(), origQty=order.getQty(), lastExecutedQty=order.getQty(), sumExecutedQty=order.getQty(), price=order.getStopPrice(), orderState=OrderState.FILLED, orderTmstmp=candle.getCloseTime(), clientOrderId=order.getIntOrderRef(), extOrderRef=order.getIntOrderRef()) self.exchangeDataListener.processOrderUpdate(response) self._updatePosition(response) # evaluate take profit market/limit orders if order.getOrderState() == OrderState.OPENED and \ order.getOrderType() in [OrderType.TAKE_PROFIT_LIMIT, OrderType.TAKE_PROFIT_MARKET] and \ ( (order.getOrderSide() == OrderSide.SELL and candle.getLow() >= order.getStopPrice()) or (order.getOrderSide() == OrderSide.BUY and candle.getHigh() <= order.getStopPrice()) ): self.log.info('>>> [' + str(candle) + ']') self.log.info('Filling take profit order ' + str(order.getOrderId())) response = OrderResponse( baseAsset=trade.getBaseAsset(), quoteAsset=trade.getQuoteAsset(), orderSide=order.getOrderSide(), orderType=order.getOrderType(), origQty=order.getQty(), lastExecutedQty=order.getQty(), sumExecutedQty=order.getQty(), price=order.getStopPrice(), orderState=OrderState.FILLED, orderTmstmp=candle.getCloseTime(), clientOrderId=order.getIntOrderRef(), extOrderRef=order.getIntOrderRef()) self.exchangeDataListener.processOrderUpdate(response) self._updatePosition(response) # if there is no order pending confirmation, do not reiterate if not self._existOrderPendingConf(): break def setCretenExecDetlId(self, cretenExecDetlId): self.cretenExecDetlId = cretenExecDetlId def _existOrderPendingConf(self): for order in self.orderManager.getLiveOrderCache().values(): if order.getOrderState() in [OrderState.OPEN_PENDING_EXT, OrderState.CANCEL_PENDING_EXT] or \ (order.getOrderState() == OrderState.OPENED and order.getOrderType() == OrderType.MARKET): return True return False def _findTrade(self, tradeId): for trade in self.orderManager.getLiveTradeCache().values(): if trade.getTradeId() == tradeId: return trade return None def _updatePosition(self, orderResponse): basePosition = self.portfolioManager.getPosition( orderResponse.getBaseAsset()) quotePosition = self.portfolioManager.getPosition( orderResponse.getQuoteAsset()) if orderResponse.getOrderSide() == OrderSide.BUY: basePosition.setFree(basePosition.getFree() + float(orderResponse.getOrigQty())) quotePosition.setFree(quotePosition.getFree() - float(orderResponse.getOrigQty() * orderResponse.getPrice())) else: basePosition.setFree(basePosition.getFree() - float(orderResponse.getOrigQty())) quotePosition.setFree(quotePosition.getFree() + float(orderResponse.getOrigQty() * orderResponse.getPrice())) self.exchangeDataListener.processPortfolioUpdate(basePosition) self.exchangeDataListener.processPortfolioUpdate(quotePosition)
class ExchangeDataListener: __metaclass__ = ABCMeta def __init__(self, exchangeClient, marketDataManager, marketRulesManager, portfolioManager, orderManager): self.exchangeClient = exchangeClient self.marketDataManager = marketDataManager self.marketRulesManager = marketRulesManager self.portfolioManager = portfolioManager self.orderManager = orderManager self.candleSubscriptions = [] self.candleListenerCallbackMap = {} self.callbackPortfolio = None self.callbackOrders = None self.cretenExecDetlId = None self.lock = RLock() self.log = Logger() @abstractmethod def start(self): pass @abstractmethod def parseCandleUpdate(self, msg): pass @abstractmethod def parseOrderUpdate(self, msg): pass @abstractmethod def parsePortfolioUpdate(self, msg): pass def setCretenExecDetlId(self, id): self.cretenExecDetlId = id def resetCandleListener(self): self.candleSubscriptions = [] self.candleListenerCallbackMap = {} def resetUserDataListener(self): self.callbackPortfolio = None self.callbackOrders = None def registerCandleListener(self, pair, cretenInterval, callback): self.candleSubscriptions.append( CandleSubscriptionKey(pair, cretenInterval)) self.candleListenerCallbackMap[CandleSubscriptionKey( pair, cretenInterval)] = makeList(callback) def registerUserDataListener(self, callbackPortfolio=None, callbackOrders=None): self.callbackPortfolio = makeList(callbackPortfolio) self.callbackOrders = makeList(callbackOrders) def processCandleUpdate(self, msg): with self.lock: self.log.debug('Candle message received: ' + str(msg)) try: # 1. parse incoming message candle = self.parseCandleUpdate(msg) # 2. update market data manager self.marketDataManager.processCandle(candle) # 3. invoke custom callbacks for callback in self.candleListenerCallbackMap[ CandleSubscriptionKey( Pair(candle.getBaseAsset(), candle.getQuoteAsset()), candle.getInterval())]: callback(candle) Db.Session().commit() except: self.log.error('Candle processing failed! Msg [' + str(msg) + ']') Db.Session().rollback() raise finally: Db.Session.remove() try: # 4. create outbound orders self.orderManager.sendOrders(self.cretenExecDetlId) Db.Session().commit() except: self.log.error( 'Candle processing failed while generating outbound orders! Msg [' + str(msg) + ']') Db.Session().rollback() raise finally: Db.Session.remove() def processOrderUpdate(self, msg): with self.lock: try: orderResponse = self.parseOrderUpdate(msg) # TODO verify that the order is incoming from this instance of creten # process only orders originating from creten if orderResponse.getClientOrderId( )[:len(Settings.ORDER_REFERENCE_PREFIX )] == Settings.ORDER_REFERENCE_PREFIX: self.orderManager.processOrderUpdate(orderResponse) if self.callbackOrders: for callback in self.callbackOrders: callback(orderResponse) else: self.log.info( 'Order message received for an order not originating from Creten. Msg [' + str(msg) + ']') Db.Session().commit() except: self.log.error('Order update processing failed! Msg [' + str(msg) + ']') Db.Session().rollback() raise finally: Db.Session.remove() try: # create outbound orders self.orderManager.sendOrders(self.cretenExecDetlId) Db.Session().commit() except: self.log.error( 'Order update processing failed while generating outbound orders! Msg [' + str(msg) + ']') Db.Session().rollback() raise finally: Db.Session.remove() def processPortfolioUpdate(self, msg): with self.lock: try: positions = self.parsePortfolioUpdate(msg) for position in positions: self.portfolioManager.addPosition(position) if self.callbackPortfolio: for callback in self.callbackPortfolio: callback(position) Db.Session().commit() except: self.log.error('Portfolio update processing failed! Msg [' + str(msg) + ']') Db.Session().rollback() raise finally: Db.Session.remove()
class DispenserAdapter: DISPENSERS = { "PULOON-LCDM1000": {"class": "Dispenser_LCDM1000", "path": "services.dispenser.dispensers.puloon_lcdm1000"}, "PULOON-LCDM2000": {"class": "Dispenser_LCDM2000", "path": "services.dispenser.dispensers.puloon_lcdm2000"}, "PULOON-LCDM4000": {"class": "Dispenser_LCDM4000", "path": "services.dispenser.dispensers.puloon_lcdm4000"}, } serial = None # Serial Object dispenser = None # Dispenser model implementation def __init__(self, dispenser_model, port=False, baudrate=False, timeout=0.6, parity=False): if dispenser_model not in self.DISPENSERS: raise DispenserError("%s is not supported. See DispenserController.DISPENSERS for available dispensers.") self.logger = Logger() self.dispenser = self.__import_class( self.DISPENSERS[dispenser_model]["path"], self.DISPENSERS[dispenser_model]["class"] )() # Set data if port: self.dispenser.port = port if baudrate: self.dispenser.baudrate = baudrate if timeout: self.dispenser.timeout = timeout if parity: self.dispenser.parity = parity @staticmethod def __import_class(class_path, class_name): try: module = __import__(class_path, fromlist="*") return getattr(module, class_name) except Exception as e: message = "DispenserAdapter: Couldnt load class %s from path %s With error: %s" % ( class_name, class_path, str(e.args), ) if __debug__: print message raise ImportError(message) def connect(self): """ Connect to serial port """ self.logger.debug("DispenserAdapter: port is %s" % self.dispenser.port) self.logger.debug("DispenserAdapter: parity is %s" % self.dispenser.parity) self.logger.debug("DispenserAdapter: baudrate is %s" % self.dispenser.baudrate) self.logger.debug("DispenserAdapter: timeout is %s" % self.dispenser.timeout) try: self.serial = serial.serial_for_url( self.dispenser.port, self.dispenser.baudrate, parity=self.dispenser.parity, timeout=self.dispenser.timeout, ) except SerialException as e: raise DispenserError("Error while connecting to dispenser", 500) try: self.status() except: pass def status(self): self.logger.debug("DispenserAdapter: get_status command received") response = self.__send(command=self.dispenser.command_status, validator=self.dispenser.validate_status_response) self.__write([self.dispenser.p_ack]) if isinstance(self.dispenser, Dispenser_LCDM4000): """ Read the EOT for LCDM4000 """ try: self.__read(timeout=0.6) except Exception as e: self.logger.debug("DispenserAdapter::status [forr Puloon_LCDM4000] error catched. [%s]" % str(e.args)) if not response: return False if "status" in response: self.logger.debug("DispenserAdapter: [CMD STATUS] result: %s" % response["status"]) self.logger.debug("DispenserAdapter: [CMD STATUS] info : %s" % response) return response["status"] return False def init(self): self.logger.debug("DispenserAdapter: init command received") response = self.__send(command=self.dispenser.command_init, validator=self.dispenser.validate_init_response) self.__write([self.dispenser.p_ack]) if isinstance(self.dispenser, Dispenser_LCDM4000): """ Read the EOT for LCDM4000 """ time.sleep(2.1) # try: # self.__read( timeout = .7 ) # except Exception as e: # self.logger.debug('DispenserAdapter::init [for Puloon_LCDM4000] error catched. [%s]' % str(e.args)) self.logger.debug("DispenserAdapter: init finished with result %s" % bool(response)) if response: return True return False def purge(self): self.logger.debug("DispenserAdapter: purge command received") response = self.__send(command=self.dispenser.command_purge, validator=self.dispenser.validate_purge_response) self.__write([self.dispenser.p_ack]) if isinstance(self.dispenser, Dispenser_LCDM4000): """ Read the EOT for LCDM4000 """ try: self.__read(timeout=0.6) except Exception as e: self.logger.debug("DispenserAdapter::purge [for Puloon_LCDM4000] error catched. [%s]" % str(e.args)) if response: if "status" in response: self.logger.debug("DispenserAdapter: [CMD PURGE] result: %s" % response["status"]) self.logger.debug("DispenserAdapter: [CMD PURGE] info : %s" % response) return response["status"] return False def dispense(self, amount, cassets_info): """ Dispense the bills amount: amount to dispense cassets_info: Cassets infrormation in format {id_cassete : {'command' : 0x45, 'denomination' : 0, 'charging' : 100, 'position' : 'upper'},...} """ dispense_info = self.dispenser.get_dispense(amount, cassets_info) command = None self.logger.debug("DispenserAdapter: Starting dispense of amount %s" % amount) # Amount is not dispensable if dispense_info["to_dispense"] == amount: raise DispenserError("Amount is not dispensable", 500) if len(dispense_info["cassets"]) > 1: command = self.dispenser.command_dispense_several(cassets_info, dispense_info["cassets"]) else: id_cassete, banknotes_count = dispense_info["cassets"].popitem() command = self.dispenser.command_dispense_single(cassets_info, id_cassete, banknotes_count) response = self.__send(command=command, validator=self.dispenser.validate_dispense_response) if not response: raise DispenserError("Error while dispensing (response from dispenser is None)", 500) self.__write([self.dispenser.p_ack]) return response def __send(self, command, validator=False): if not self.serial: raise DispenserError("Serial port is not opened") self.logger.debug("DispenserAdapter: send command in hex %s" % (["%02X" % x for x in command])) """ Пишем, спим полсекундочки и читаем """ self.logger.debug(">> serial.write %s" % (["%02X" % x for x in command])) bytes_written = self.__write(command) self.logger.debug(">> serial.read") bytes_recd = self.__read(timeout=1) self.logger.debug(">> serial.read done") if bytes_recd == [self.dispenser.p_nck]: raise DispenserError("DispenserAdapter.__send: bytes_recd == NCK") if bytes_recd == [self.dispenser.p_ack]: self.logger.debug("DispenserAdapter.__send: bytes_recd == ACK") bytes_recd = self.__read(timeout=60) readed = ["%02X" % x for x in bytes_recd] else: if isinstance(self.dispenser, Dispenser_LCDM4000): self.logger.debug("Dispenser instance is Dispenser_LCDM4000. F**k this!") if validator: return validator(bytes_recd) readed = ["%02X" % x for x in bytes_recd] self.logger.debug("DispenserAdapter.__send [read from dispenser]: HEX is %s" % readed) raise DispenserError("Shit hap in HEX: %s" % readed) self.logger.debug(">> serial.read in HEX: %s" % readed) # if isinstance(self.dispenser, Dispenser_LCDM4000): # ''' Read the EOT for LCDM4000 ''' # try: # self.__read( timeout = .5 ) # except Exception as e: # self.logger.debug('DispenserAdapter::dispense [for Puloon_LCDM4000] error catched. [%s]' % str(e.args)) if validator: return validator(bytes_recd) self.logger.debug("DispenserAdapter: No validator presents. Return invalidated response.") return bytes_recd def __write(self, bytes): return self.serial.write(serial.to_bytes(bytes)) def __read(self, timeout=60): time_start = time.time() frame = [] data = "" byte = self.serial.read(1) data += byte try: first_byte = int(byte2hex(byte), 16) self.logger.debug("DispenserAdapter.__read: first byte is: 0x%02X" % first_byte) if first_byte == self.dispenser.p_nck: self.logger.debug("DispenserAdapter.__read: first byte is NCK") return [self.dispenser.p_nck] if first_byte == self.dispenser.p_ack: self.logger.debug("DispenserAdapter.__read: first byte is: ACK") return [self.dispenser.p_ack] byte = self.serial.read(1) data += byte self.logger.debug("DispenserAdapter.__read: Wrong first byte in response 0x%02X" % first_byte) self.logger.debug("DispenserAdapter.__read: Next byte in response is 0x%02X" % int(byte2hex(byte))) except Exception as e: self.logger.debug("DispenserAdapter.__read: %s" % str(e.args)) pass self.logger.debug("DispenserAdapter.__read: first byte is detected") while True: if time.time() - time_start > timeout: self.logger.debug("DispenserAdapter.__read: timeout to read. ") for _, i in enumerate(serial.to_bytes(data)): frame.append(int(byte2hex(i), 16)) self.logger.debug( "DispenserAdapter.__read: full dump of response: %s" % (["0x%02X" % x for x in frame]) ) raise DispenserError(message="Timeout to read", code=500) try: if int(byte2hex(byte), 16) == self.dispenser.p_etx: break except Exception as e: self.logger.debug("Error in read cycle: %s" % str(e.args)) time.sleep(0.01) byte = self.serial.read(1) data += byte self.logger.debug("DispenserAdapter.__read: cycle is complete") data += self.serial.read(1) for _, i in enumerate(serial.to_bytes(data)): frame.append(int(byte2hex(i), 16)) self.logger.debug("DispenserAdapter.__read: full dump of response: [%s]" % ([x for x in frame])) return frame