@@ -118,15 +118,15 @@ def cancel(self, order):
118118 except :
119119 self ._logger .exception ('[%s] 撤单失败' , trader .id )
120120
121- def sync (self ):
121+ def work (self ):
122122 stop_watch = StopWatch ()
123123 stop_watch .start ()
124- self ._logger .info ("[%s] 开始同步 " , self ._id )
124+ self ._logger .info ("[%s] 开始工作 " , self ._id )
125125 self ._refresh ()
126126 for id , trader in self ._traders .items ():
127- trader .sync ()
127+ trader .work ()
128128 stop_watch .stop ()
129- self ._logger .info ("[%s] 结束同步 ,总耗时[%s]" , self ._id , stop_watch .short_summary ())
129+ self ._logger .info ("[%s] 结束工作 ,总耗时[%s]" , self ._id , stop_watch .short_summary ())
130130 self ._logger .info (self .THEMATIC_BREAK )
131131
132132 def _refresh (self ):
@@ -142,7 +142,7 @@ def __init__(self, logger, config, strategy_context):
142142 self ._config = config
143143 self ._strategy_context = strategy_context
144144 self ._shipane_client = Client (self ._logger , ** config ['client' ])
145- self ._order_id_map = {}
145+ self ._order_id_to_info_map = {}
146146 self ._expire_before = datetime .datetime .combine (datetime .date .today (), datetime .time .min )
147147 self ._last_sync_portfolio_fingerprint = None
148148
@@ -158,38 +158,45 @@ def set_config(self, config):
158158 self ._config = config
159159
160160 def purchase_new_stocks (self ):
161+ if not self ._pre_check ():
162+ return
163+
161164 self ._shipane_client .purchase_new_stocks ()
162165
163166 def execute (self , order = None , ** kwargs ):
167+ if not self ._pre_check ():
168+ return
169+
164170 if order is None :
165171 common_order = Order .from_e_order (** kwargs )
166172 else :
167173 common_order = self ._normalize_order (order )
168174
169- self ._logger .info ("[实盘易] 跟单:" + str (common_order ))
170- if not self ._should_execute (common_order ):
171- return
172-
173175 try :
174176 actual_order = self ._execute (common_order )
175177 return actual_order
176178 except Exception :
177179 self ._logger .exception ("[实盘易] 下单异常" )
178180
179181 def cancel (self , order ):
180- if order is None :
181- self ._logger .info ('[实盘易] 委托为空,忽略撤单请求' )
182+ if not self ._pre_check ():
182183 return
183184
184185 try :
185186 self ._cancel (order )
186187 except :
187188 self ._logger .exception ("[实盘易] 撤单异常" )
188189
189- def sync (self ):
190+ def work (self ):
190191 if not self ._pre_check ():
191192 return
192193
194+ if self ._config ['mode' ] == 'SYNC' :
195+ self ._sync ()
196+ else :
197+ self ._follow ()
198+
199+ def _sync (self ):
193200 stop_watch = StopWatch ()
194201 stop_watch .start ()
195202 self ._logger .info ("[%s] 开始同步" , self .id )
@@ -199,7 +206,7 @@ def sync(self):
199206 self ._logger .info ("[%s] 模拟盘撤销全部订单已完成" , self .id )
200207 target_portfolio = self ._strategy_context .get_portfolio ()
201208 if self ._should_sync (target_portfolio ):
202- if self ._sync_config ['pre-clear-for-live' ] and not self ._sync_config ['dry-run' ]:
209+ if self ._sync_config ['pre-clear-for-live' ] and not self ._config ['dry-run' ]:
203210 self ._shipane_client .cancel_all ()
204211 time .sleep (self ._sync_config ['order-interval' ] / 1000.0 )
205212 self ._logger .info ("[%s] 实盘撤销全部订单已完成" , self .id )
@@ -220,27 +227,81 @@ def sync(self):
220227 stop_watch .stop ()
221228 self ._logger .info ("[%s] 结束同步,耗时[%s]" , self .id , stop_watch .short_summary ())
222229
230+ def _follow (self ):
231+ stop_watch = StopWatch ()
232+ stop_watch .start ()
233+ self ._logger .info ("[%s] 开始跟单" , self .id )
234+ try :
235+ common_orders = []
236+ all_common_orders = self ._strategy_context .get_orders ()
237+ for common_order in all_common_orders :
238+ if common_order .add_time >= self ._strategy_context .get_current_time ():
239+ if common_order .status == OrderStatus .canceled :
240+ origin_order = copy .deepcopy (common_order )
241+ origin_order .status = OrderStatus .open
242+ common_orders .append (origin_order )
243+ else :
244+ common_orders .append (common_order )
245+ if common_order .status == OrderStatus .canceled :
246+ common_orders .append (common_order )
247+
248+ common_orders = sorted (common_orders , key = lambda o : _PrioritizedOrder (o ))
249+ for common_order in common_orders :
250+ if common_order .status != OrderStatus .canceled :
251+ try :
252+ self ._execute (common_order )
253+ except :
254+ self ._logger .exception ("[实盘易] 下单异常" )
255+ else :
256+ try :
257+ self ._cancel (common_order )
258+ except :
259+ self ._logger .exception ("[实盘易] 撤单异常" )
260+ except :
261+ self ._logger .exception ("[%s] 跟单失败" , self .id )
262+ stop_watch .stop ()
263+ self ._logger .info ("[%s] 结束跟单,耗时[%s]" , self .id , stop_watch .short_summary ())
264+
223265 @property
224266 def _sync_config (self ):
225267 return self ._config ['sync' ]
226268
227269 def _execute (self , order ):
270+ if not self ._should_run ():
271+ self ._logger .info ("[%s] %s" , self .id , order )
272+ return None
273+ actual_order = self ._do_execute (order )
274+ return actual_order
275+
276+ def _cancel (self , order ):
277+ if not self ._should_run ():
278+ self ._logger .info ("[%s] 撤单 [%s]" , self .id , order )
279+ return
280+ self ._do_cancel (order )
281+
282+ def _do_execute (self , order ):
228283 common_order = self ._normalize_order (order )
229284 e_order = common_order .to_e_order ()
230285 actual_order = self ._shipane_client .execute (** e_order )
231- self ._order_id_map [common_order .id ] = actual_order ['id' ]
286+ self ._order_id_to_info_map [common_order .id ] = { 'id' : actual_order ['id' ], 'canceled' : False }
232287 return actual_order
233288
234- def _cancel (self , order ):
289+ def _do_cancel (self , order ):
290+ if order is None :
291+ self ._logger .info ('[实盘易] 委托为空,忽略撤单请求' )
292+ return
293+
235294 if isinstance (order , int ):
236295 quant_order_id = order
237296 else :
238297 common_order = self ._normalize_order (order )
239298 quant_order_id = common_order .id
240299
241300 try :
242- order_id = self ._order_id_map .pop (quant_order_id )
243- self ._shipane_client .cancel (order_id = order_id )
301+ order_info = self ._order_id_to_info_map [quant_order_id ]
302+ if not order_info ['canceled' ]:
303+ order_info ['canceled' ] = True
304+ self ._shipane_client .cancel (order_id = order_info ['id' ])
244305 except KeyError :
245306 self ._logger .warning ('[实盘易] 未找到对应的委托编号' )
246307
@@ -251,27 +312,21 @@ def _normalize_order(self, order):
251312 common_order = self ._strategy_context .convert_order (order )
252313 return common_order
253314
254- def _should_execute (self , common_order ):
255- if self ._strategy_context .is_backtest ():
256- self ._logger .info ("[实盘易] 当前为回测环境,忽略下单请求" )
257- return False
258- if common_order is None :
259- self ._logger .info ('[实盘易] 委托为空,忽略下单请求' )
260- return False
261- if self ._is_expired (common_order ):
262- self ._logger .info ('[实盘易] 委托已过期,忽略下单请求' )
315+ def _should_run (self ):
316+ if self ._config ['dry-run' ]:
317+ self ._logger .debug ("[实盘易] 当前为排练模式,不执行下单、撤单请求" )
263318 return False
264319 return True
265320
266321 def _is_expired (self , common_order ):
267322 return common_order .add_time < self ._expire_before
268323
269324 def _pre_check (self ):
270- if not self ._sync_config ['enabled' ]:
271- self ._logger .info ("[%s] 同步未启用,不进行同步 " , self .id )
325+ if not self ._config ['enabled' ]:
326+ self ._logger .info ("[%s] 同步未启用,不执行 " , self .id )
272327 return False
273328 if self ._strategy_context .is_backtest ():
274- self ._logger .info ("[%s] 当前为回测环境,不进行同步 " , self .id )
329+ self ._logger .info ("[%s] 当前为回测环境,不执行 " , self .id )
275330 return False
276331 return True
277332
@@ -316,21 +371,10 @@ def _log_progress(self, adjustment):
316371 def _execute_adjustment (self , adjustment ):
317372 for batch in adjustment .batches :
318373 for order in batch :
319- self ._execute_order (order )
374+ self ._execute (order )
320375 time .sleep (self ._sync_config ['order-interval' ] / 1000.0 )
321376 time .sleep (self ._sync_config ['batch-interval' ] / 1000.0 )
322377
323- def _execute_order (self , order ):
324- try :
325- if self ._sync_config ['dry-run' ]:
326- self ._logger .info ("[%s] %s" , self .id , order )
327- return
328-
329- e_order = order .to_e_order ()
330- self ._shipane_client .execute (** e_order )
331- except :
332- self ._logger .exception ("[%s] 客户端下单失败" , self .id )
333-
334378
335379class StrategyConfig (object ):
336380 def __init__ (self , strategy_context ):
@@ -368,14 +412,10 @@ def _create_proxy_configs(self):
368412
369413 def _create_trader_config (self , raw_trader_config ):
370414 client_config = self ._create_client_config (raw_trader_config )
371- sync_config = raw_trader_config ['sync' ]
372- sync_config ['reserved-securities' ] = client_config ['reserved_securities' ]
373- result = {
374- 'id' : raw_trader_config ['id' ],
375- 'client' : client_config ,
376- 'sync' : sync_config
377- }
378- return result
415+ trader_config = copy .deepcopy (raw_trader_config )
416+ trader_config ['client' ] = client_config
417+ trader_config ['sync' ]['reserved-securities' ] = client_config .pop ('reserved_securities' , [])
418+ return trader_config
379419
380420 def _create_client_config (self , raw_trader_config ):
381421 client_config = None
@@ -410,3 +450,39 @@ def _create_client_config(self, raw_trader_config):
410450 if client_config is not None :
411451 break
412452 return client_config
453+
454+
455+ class _PrioritizedOrder (object ):
456+ def __init__ (self , order ):
457+ self .order = order
458+
459+ def __lt__ (self , other ):
460+ x = self .order
461+ y = other .order
462+ if x .add_time != y .add_time :
463+ return x .add_time < y .add_time
464+ if x .status == OrderStatus .canceled :
465+ if y .status == OrderStatus .canceled :
466+ return x .id < y .id
467+ else :
468+ return False
469+ else :
470+ if y .status == OrderStatus .canceled :
471+ return True
472+ else :
473+ return x .id < y .id
474+
475+ def __gt__ (self , other ):
476+ return other .__lt__ (self )
477+
478+ def __eq__ (self , other ):
479+ return (not self .__lt__ (other )) and (not other .__lt__ (self ))
480+
481+ def __le__ (self , other ):
482+ return not self .__gt__ (other )
483+
484+ def __ge__ (self , other ):
485+ return not self .__lt__ (other )
486+
487+ def __ne__ (self , other ):
488+ return not self .__eq__ (other )
0 commit comments