Bitfinex code

不同的类共享相同的log level,但是self.log是不同的文件。
所有的log.error是不是需要什么机制报警?比如customize logging


可以看出,https://bitfinex.readme.io/v2/reference#ws-public-ticker 里面所有的public channel都有request/response和snapshot/update,而authenticated channel里面都只有snapshot/update
在代码里面表现为,所有的public channel request/response subscibe/subscribed event都是dict,经由WebSocketConnection ::_response_handler交给QueueProcessor::_response_handlers处理,使用data.pop('chanId')的方式获得数据,主要任务是处理self.channel_directory,即维护chanId<->chan_identifier之间的映射;
其他的所有public/authenticated channel snapshot/update都是list,经由WebSocketConnection ::_msg_handler交给QueueProcessor::run()使用data[0]的方式提取出channel_id分别交给_handle_public_channel()或_handle_account()处理。

实际上相当于connection完成连接,把msg event转发给queue_processor处理;BtfxWss更像是一个wrapper,实现这些功能;真正的client使用client.py中的tickers()/books()/etc.等函数的返回值,进行处理。【还是可以再写成event driven。。。】


WebSocketConnection
connection作为thread.run(),一直调用_connect(), 里面保证self.socket连接不断;使用event-driven方式,根据状态维持连接。比如收到info,handle时候控制connected/reconnect_required等Event表示状态,在各种连接状态控制里面使用。

_on_open(),发ping,验证是否recconect_required
_on_close(),connected.clear(), _stop_timer()
_on_error(), 设置recconect_required,清理connected

_on_message()
library假设只要json.loads(raw)是dict类型,event必须是pong/info/error, subscribed/unsubscribed/conf/auth/unauth中的一种;如果不是dict类型,则_data_handler传递给queue_processor处理。

_heartbeat_handler(), 调用_stop_timers(),重新start ping/connection timer.
_pong_handler() set pong_received=True, which will be checked in _check_pong();
_info_handler() 如果有version,直接输出返回;否则按照20051/20060/20061分别调用相应的reconnect(), _pause(), _unpause()函数 https://docs.bitfinex.com/v2/docs/ws-general#section-info-messages;
_error_handler(), https://docs.bitfinex.com/v2/docs/ws-general#section-error-codes , 代码里只是log一下,所有的error codes在这个页面都有,实际开发中,如果有error codes,使用某种方法进行预警;这种error是error event, 还有error根本就不是_on_message(),直接_on_error(). 后一种情形一般connection status,需要reconnect;code 10001可能也表示Generic error/Unknown pair.
_system_handler()的剩余events被当作response,和_data_handler()一样,直接传递给queue_processor处理。

https://docs.bitfinex.com/v2/docs/ws-general#section-info-messages  要么是
{
   "event": "info",
   "version":  VERSION
}
要么是 
{
   "event":"info",
   "code": CODE,
   "msg": MSG
}

虽然代码里面没有使用version,但是文档建议,如果使用trading bot,最好控制version number changes,也就是注意log里面msg['version']的变化.

WebSocketConnection._resubscribe(soft=False)
resubscribe尚存的channels;  ordereddict LIFO if last=True else FIFO,这里是LIFO所有后面是reversed.
self.channel_configs是一个dict,维护着channels;
append参数是.copy()是怕传引用;
如果是'auth' channel,调用send(**q, auth=True),也就是不会unsubscribe;
如不是'auth' channel,看是否soft来确定是否先unsubscribe.
处理完所有channels后,重新存入channel_config;如果soft,需要重新send,也就是重新subscribe()?


WebSocketConnection.send() 这个函数的好处在于,反正都是向self.socket发数据,给予caller足够的调用自由度,同一个接口实现很多种send。
调用方式是self.send(**q, auth=False/True),从client._subscribe/_unsubscribe看到q={'event':'', 'channel':''}的格式。
向connection发送pyload数据;
如果是auth,说明需要key&secret,加密后发送;否则发送list_data或**kwargs;
发给self.socket;处理异常WebSocketConnectionClosedException







queue_processor 也继承Thread
需要看并比对一下data与msg,不能乱改,有可能官方就是'data' key.

__init__()这也太。。。WebSocketConnection.__init__()使用Thread.__init__(self)初始化父类,QueueProcessor.__init__()使用super(QP, self).__init(*args, **kwargs)

run()
如果是走_response_handler()进来,直接调用相关handler;如果是data类型,从self.channel_directory里面找到channel_type;
queue_processor的self.account_channel_names其实是channel type=key,但是过于简化,value提供更加self-explained的名字。

做法相当于是不同的events进入不同的dict, 如self.ticker, books, raw_books, trades, candles, account; 前几个都是public channel,属于_data_handlers; account channel是chanId=0, 包括所有的authenticated channels, 属于account_channel_names;所有这些events都是data event
dict的每个元素都是Queue,相同的channel_identifier进入相同的queue,client.py进行consume。

_handle_conf()只是logging
_handle_subscribed()和_handle_auth()赋值self.channel_directory,_handle_unsubscribed()删除self.channel_directory和self.last_update的相应key. 
_handle_public_channel(), 从self.channel_directory获得相应的channel_identifier,并self.defaultdict[channel_identifier].put(data, ts)
_handle_account()将所有的data放入[channel_identifier] Queue中


BtfxWss
不同的thread分别用于connection和data handling;
start/stop/reset,self.conn只管连接状态,self.queue_processor只管处理数据的线程;
reset()调用self.conn.reconnect()会self.connected.clear(),但是reset()函数里面会验证self.conn.connnected,所以应该是socket 连接会调用_on_open()函数?
【在_connect()函数里面,while loop检查self.reconnect_required.is_set()然后等socket的连接?】反正self.conn.reconnect()就是完成了重新连接的功能;
整个reset()函数就是重新连接,重新发channel_configs数据。

tickers/books/raw_books/trades/candles等函数就是拿pair参数,返回相应的Queue.

_subscribe/_unsubscribe是util函数,但是subscribe时候发送channel_name,unsubscribe时候发送channel_id;然后是subscribe/unsubscribe各种public channels
config()函数就是设置config,向socket发conf event,参数是flags【这里是发,connection.py收到response,queue_processor只是打一下log】;

authenticate(),设置channel_configs即WebSocketConnection.channel_configs,然后发【问题是谁调用这个函数?调用完之后干嘛?看test代码】
new_order()/cancel_order/order_multi_op/calc调用_send_auth_command()发送new order/cancel order/multi/operations.



README.rst的意思是先subscribe,然后再ping?

Code

10-11

DescriptionnnKEY Inc., the leading company in security hardware, has developed a new kind of safe. To unlock it, you don't need a key but you are required to enter the correct n-digit code on a keypad (as if this were something new!). There are several models available, from toy safes for children (with a 2-digit code) to the military version (with a 6-digit code). nnThe safe will open as soon as the last digit of the correct code is entered. There is no "enter" key. When you enter more than n digits, only the last n digits are significant. For example (in the 4-digit version), if the correct code is 4567, and you plan to enter the digit sequence 1234567890, the door will open as soon as you press the 7 key. nnThe software to create this effect is rather simple. In the n-digit version the safe is always in one of 10n-1 internal states. The current state of the safe simply represents the last n-1 digits that have been entered. One of these states (in the example above, state 456) is marked as the unlocked state. If the safe is in the unlocked state and then the right key (in the example above, 7) is pressed, the door opens. Otherwise the safe shifts to the corresponding new state. For example, if the safe is in state 456 and then you press 8, the safe goes into state 568. nnA trivial strategy to open the safe is to enter all possible codes one after the other. In the worst case, however, this will require n * 10n keystrokes. By choosing a good digit sequence it is possible to open the safe in at most 10n + n - 1 keystrokes. All you have to do is to find a digit sequence that contains all n-digit sequences exactly once. KEY Inc. claims that for the military version (n=6) the fastest computers available today would need billions of years to find such a sequence - but apparently they don't know what some programmers are capable of...nInputnnThe input contains several test cases. Every test case is specified by an integer n. You may assume that 1<=n<=6. The last test case is followed by a zero.nOutputnnFor each test case specified by n output a line containing a sequence of 10n + n - 1 digits that contains each n-digit sequence exactly once.nSample Inputnn1n2n0nSample Outputnn0123456789n00102030405060708091121314151617181922324252627282933435363738394454647484955657585966768697787988990 问答

没有更多推荐了,返回首页