Exemplo n.º 1
0
    def __init__(self):
        # TODO heap is better for insertion and deletion. We only need implement search of heapq module.
        self.__orders = dict()
        self.seq_gen = SequenceGenerator()

        self.date = 0
        self.time = 0
Exemplo n.º 2
0
    def __init__(self):
        super(BacktestTradeApi, self).__init__()

        self.ctx = None

        self._orderbook = OrderBook()
        self.seq_gen = SequenceGenerator()
        self.entrust_no_task_id_map = dict()

        self.commission_rate = 0.0
Exemplo n.º 3
0
    def __init__(self):
        super(RealTimeTradeApi_async, self).__init__()

        self.ctx = None

        self._trade_api = None

        self._task_no_id_map = dict()

        self.seq_gen = SequenceGenerator()
Exemplo n.º 4
0
    def __init__(self):
        super(AlphaTradeApi, self).__init__()
        self.ctx = None

        self._simulator = DailyStockSimulator()
        self.entrust_no_task_id_map = dict()
        self.seq_gen = SequenceGenerator()

        self.commission_rate = 0.0

        self.MATCH_TIME = 143000
Exemplo n.º 5
0
    def __init__(self):
        super(Strategy, self).__init__()
        self.ctx = None
        # self.run_mode = common.RUN_MODE.BACKTEST

        # self.ctx.pm = PortfolioManager(strategy=self)
        # self.pm = self.ctx.pm

        # self.task_id_map = defaultdict(list)
        self.seq_gen = SequenceGenerator()

        self.init_balance = 0.0
Exemplo n.º 6
0
    def __init__(self):
        self.context = None
        self.run_mode = common.RUN_MODE.BACKTEST

        self.pm = PortfolioManager(self)

        self.task_id_map = defaultdict(list)
        self.seq_gen = SequenceGenerator()

        self.trade_date = 0

        self.init_balance = 0.0
Exemplo n.º 7
0
 def __init__(self):
     # TODO heap is better for insertion and deletion. We only need implement search of heapq module.
     self.__orders = dict()
     self.seq_gen = SequenceGenerator()
     
     self.date = 0
     self.time = 0
Exemplo n.º 8
0
 def __init__(self):
     super(BacktestTradeApi, self).__init__()
     
     self.ctx = None
     
     self._orderbook = OrderBook()
     self.seq_gen = SequenceGenerator()
     self.entrust_no_task_id_map = dict()
     
     self.commission_rate = 0.0
Exemplo n.º 9
0
    def __init__(self):
        super(AlphaTradeApi, self).__init__()
        self.ctx = None
        
        self._simulator = DailyStockSimulator()
        self.entrust_no_task_id_map = dict()
        self.seq_gen = SequenceGenerator()

        self.commission_rate = 0.0
        
        self.MATCH_TIME = 143000
Exemplo n.º 10
0
    def __init__(self):
        super(Strategy, self).__init__()
        self.ctx = None
        # self.run_mode = common.RUN_MODE.BACKTEST
        
        # self.ctx.pm = PortfolioManager(strategy=self)
        # self.pm = self.ctx.pm

        # self.task_id_map = defaultdict(list)
        self.seq_gen = SequenceGenerator()

        self.init_balance = 0.0
Exemplo n.º 11
0
class BacktestTradeApi(BaseTradeApi):
    def __init__(self):
        super(BacktestTradeApi, self).__init__()

        self.ctx = None

        self._orderbook = OrderBook()
        self.seq_gen = SequenceGenerator()
        self.entrust_no_task_id_map = dict()

        self.commission_rate = 0.0

    def _get_next_num(self, key):
        """used to generate id for orders and trades."""
        return str(
            np.int64(self.ctx.trade_date) * 10000 + self.seq_gen.get_next(key))

    def _get_next_task_id(self):
        return np.int64(
            self.ctx.trade_date) * 10000 + self.seq_gen.get_next('task_id')

    def init_from_config(self, props):
        self.commission_rate = props.get('commission_rate', 0.0)

        self.set_order_status_callback(
            lambda ind: self.ctx.strategy.on_order_status(ind))
        self.set_trade_callback(lambda ind: self.ctx.strategy.on_trade(ind))
        self.set_task_status_callback(
            lambda ind: self.ctx.strategy.on_task_status(ind))

    def on_new_day(self, trade_date):
        self._orderbook = OrderBook()

    def use_strategy(self, strategy_id):
        pass

    def place_order(self,
                    security,
                    action,
                    price,
                    size,
                    algo="",
                    algo_param={},
                    userdata=""):
        if size <= 0:
            print("Invalid size {}".format(size))
            return

        # Generate Order
        if algo == 'vwap':
            order_type = common.ORDER_TYPE.VWAP
        else:
            order_type = common.ORDER_TYPE.LIMIT
        order = Order.new_order(security,
                                action,
                                price,
                                size,
                                self.ctx.trade_date,
                                self.ctx.time,
                                order_type=order_type)

        # Generate Task
        task_id = self._get_next_task_id()
        order.task_id = task_id

        task = Task(task_id,
                    algo=algo,
                    algo_param=algo_param,
                    data=order,
                    function_name='place_order',
                    trade_date=self.ctx.trade_date)
        # task.task_no = task_id

        # Send Order to Exchange
        entrust_no = self._orderbook.add_order(order)
        task.data.entrust_no = entrust_no

        self.ctx.pm.add_task(task)
        self.entrust_no_task_id_map[entrust_no] = task.task_id

        order_status_ind = OrderStatusInd(order)
        order_status_ind.order_status = common.ORDER_STATUS.ACCEPTED
        # order_status_ind.task_no = task_id
        self._order_status_callback(order_status_ind)
        '''
        # TODO: not necessary
        rsp = OrderRsp(entrust_no=entrust_no, task_id=task_id, msg="")
        self.ctx.instance.strategy.on_order_rsp(rsp)
        '''

        return task_id, ""

    def cancel_order(self, task_id):
        task = self.ctx.pm.get_task(task_id)
        if task.function_name == 'place_order':
            order = task.data
            entrust_no = order.entrust_no
            order_status_ind = self._orderbook.cancel_order(entrust_no)
            task_id = self.entrust_no_task_id_map[entrust_no]
            order_status_ind.task_id = task_id
            # order_status_ind.task_no = task_id
            self._order_status_callback(order_status_ind)
        else:
            raise NotImplementedError(
                "cancel task with function_name = {}".format(
                    task.function_name))

    def _process_quote(self, df_quote, freq):
        return self._orderbook.make_trade(df_quote, freq)

    def _add_task_id(self, ind):
        no = ind.entrust_no
        task_id = self.entrust_no_task_id_map[no]
        ind.task_id = task_id
        # ind.task_no = task_id

    def _add_commission(self, ind):
        comm = calc_commission(ind, self.commission_rate)
        ind.commission = comm

    def match_and_callback(self, quote, freq):
        results = self._process_quote(quote, freq)

        for trade_ind, order_status_ind in results:
            self._add_commission(trade_ind)

            # self._add_task_id(trade_ind)
            # self._add_task_id(order_status_ind)
            task_id = self.entrust_no_task_id_map[trade_ind.entrust_no]

            self._order_status_callback(order_status_ind)
            self._trade_callback(trade_ind)

            task = self.ctx.pm.get_task(task_id)
            if task.is_finished:
                task_ind = TaskInd(task_id,
                                   task_status=task.task_status,
                                   task_algo='',
                                   task_msg="")
                self._task_status_callback(task_ind)

        return results
Exemplo n.º 12
0
    def __init__(self):
        self.orders = dict()

        self.seq_gen = SequenceGenerator()
        self.participation_rate = 1.0
Exemplo n.º 13
0
class OrderBook(object):
    def __init__(self):
        self.orders = dict()

        self.seq_gen = SequenceGenerator()
        self.participation_rate = 1.0

    def _next_fill_no(self):
        return str(self.seq_gen.get_next('trade_id'))

    def _next_order_entrust_no(self):
        return str(self.seq_gen.get_next('order_id'))

    def add_order(self, order):
        neworder = copy.copy(order)

        entrust_no = self._next_order_entrust_no()
        neworder.entrust_no = entrust_no

        self.orders[entrust_no] = neworder

        return entrust_no

    def _make_tick_trade(self, quote):
        raise NotImplementedError()

    def make_trade(self, quote, freq):

        if freq == common.QUOTE_TYPE.TICK:
            # TODO
            return self._make_tick_trade(quote)

        elif (freq == common.QUOTE_TYPE.MIN
              or freq == common.QUOTE_TYPE.FIVEMIN
              or freq == common.QUOTE_TYPE.QUARTERMIN
              or freq == common.QUOTE_TYPE.SPECIALBAR):
            return self._make_trade_bar(quote)

        elif freq == common.QUOTE_TYPE.DAILY:
            return self._make_trade_bar(quote)

    def _make_trade_bar(self, quote_dic):

        result = []

        for entrust_no, order in self.orders.items():
            quote = quote_dic[order.symbol]
            low = quote.low
            high = quote.high
            quote_date = quote.trade_date
            quote_time = quote.time
            volume = quote.volume
            '''
            if order.order_type == common.ORDER_TYPE.LIMIT:
                if order.entrust_action == common.ORDER_ACTION.BUY and order.entrust_price >= low:
                    trade = Trade()
                    trade.init_from_order(order)
                    trade.send_fill_info(order.entrust_price, order.entrust_size,
                                         quote_date, quote_time,
                                         self._next_fill_no())
                    
                    order.order_status = common.ORDER_STATUS.FILLED
                    order.fill_size = trade.fill_size
                    order.fill_price = trade.fill_price
                    
                    orderstatus_ind = OrderStatusInd()
                    orderstatus_ind.init_from_order(order)
                    
                    result.append((trade, orderstatus_ind))
                    
                elif order.entrust_action == common.ORDER_ACTION.SELL and order.entrust_price <= high:
                    trade = Trade()
                    trade.init_from_order(order)
                    trade.send_fill_info(order.entrust_price, order.entrust_size,
                                         quote_date, quote_time,
                                         self._next_fill_no())
                    
                    order.order_status = common.ORDER_STATUS.FILLED
                    order.fill_size = trade.fill_size
                    order.fill_price = trade.fill_price
                    
                    orderstatus_ind = OrderStatusInd()
                    orderstatus_ind.init_from_order(order)

                    result.append((trade, orderstatus_ind))
            
            elif order.order_type == common.ORDER_TYPE.STOP:
                if order.entrust_action == common.ORDER_ACTION.BUY and order.entrust_price <= high:
                    trade = Trade()
                    trade.init_from_order(order)
                    trade.send_fill_info(order.entrust_price, order.entrust_size,
                                         quote_date, quote_time,
                                         self._next_fill_no())
                    
                    order.order_status = common.ORDER_STATUS.FILLED
                    order.fill_size = trade.fill_size
                    order.fill_price = trade.fill_price
                    orderstatus_ind = OrderStatusInd()
                    orderstatus_ind.init_from_order(order)
                    result.append((trade, orderstatus_ind))
                
                if order.entrust_action == common.ORDER_ACTION.SELL and order.entrust_price >= low:
                    trade = Trade()
                    trade.init_from_order(order)
                    trade.send_fill_info(order.entrust_price, order.entrust_size,
                                         quote_date, quote_time,
                                         self._next_fill_no())
                    
                    order.order_status = common.ORDER_STATUS.FILLED
                    order.fill_size = trade.fill_size
                    order.fill_price = trade.fill_price
                    orderstatus_ind = OrderStatusInd()
                    orderstatus_ind.init_from_order(order)
                    result.append((trade, orderstatus_ind))
            '''

            entrust_price = order.entrust_price
            entrust_size = order.entrust_size

            fill_size = 0
            if order.order_type == common.ORDER_TYPE.LIMIT:
                if common.ORDER_ACTION.is_positive(
                        order.entrust_action) and entrust_price >= low:
                    fill_price = min(entrust_price, high)
                    # fill_size = min(entrust_size, self.participation_rate * volume)
                    fill_size = entrust_size

                elif common.ORDER_ACTION.is_negative(
                        order.entrust_action) and order.entrust_price <= high:
                    fill_price = max(entrust_price, low)
                    # fill_size = min(entrust_size, self.participation_rate * volume)
                    fill_size = entrust_size

            elif order.order_type == common.ORDER_TYPE.STOP:
                if common.ORDER_ACTION.is_positive(
                        order.entrust_action) and order.entrust_price <= high:
                    fill_price = max(entrust_price, low)
                    # fill_size = min(entrust_size, self.participation_rate * volume)
                    fill_size = entrust_size

                if common.ORDER_ACTION.is_negative(
                        order.entrust_action) and order.entrust_price >= low:
                    fill_price = min(entrust_price, high)
                    # fill_size = min(entrust_size, self.participation_rate * volume)
                    fill_size = entrust_size

            elif order.order_type == common.ORDER_TYPE.VWAP:
                fill_price = quote.vwap
                fill_size = entrust_size

            if not fill_size:
                continue

            trade_ind = Trade(order)
            trade_ind.set_fill_info(order.entrust_price,
                                    order.entrust_size, quote_date, quote_time,
                                    self._next_fill_no())

            order.fill_price = (
                (order.fill_price * order.fill_size + fill_size * fill_price) /
                (order.fill_size + fill_size))
            order.fill_size += fill_size
            if order.fill_size == order.entrust_size:
                order.order_status = common.ORDER_STATUS.FILLED

            order_status_ind = OrderStatusInd(order)

            result.append((trade_ind, order_status_ind))

        self.orders = {
            k: v
            for k, v in self.orders.items() if not v.is_finished
        }

        return result

    def cancel_order(self, entrust_no):
        order = self.orders.pop(entrust_no)
        order.cancel_size = order.entrust_size - order.fill_size
        order.order_status = common.ORDER_STATUS.CANCELLED

        order_status_ind = OrderStatusInd(order)
        '''
        for i in xrange(len(self.orders)):
            order = self.orders[i]
            
            if (order.is_finished):
                continue
            
            if (order.entrust_no == entrust_no):
                order.cancel_size = order.entrust_size - order.fill_size
                order.order_status = common.ORDER_STATUS.CANCELLED
            
            # todo
            orderstatus = OrderStatusInd()
            orderstatus.init_from_order(order)
            
            return orderstatus
    
        '''
        return order_status_ind

    '''
Exemplo n.º 14
0
class OrderBook(object):
    def __init__(self):
        self.orders = dict()
        
        self.seq_gen = SequenceGenerator()
        self.participation_rate = 1.0
    
    def _next_fill_no(self):
        return str(self.seq_gen.get_next('trade_id'))
    
    def _next_order_entrust_no(self):
        return str(self.seq_gen.get_next('order_id'))
    
    def add_order(self, order):
        neworder = copy.copy(order)
        
        entrust_no = self._next_order_entrust_no()
        neworder.entrust_no = entrust_no
        
        self.orders[entrust_no] = neworder
        
        return entrust_no
    
    def _make_tick_trade(self, quote):
        raise NotImplementedError()
    
    def make_trade(self, quote, freq):
        
        if freq == common.QUOTE_TYPE.TICK:
            # TODO
            return self._make_tick_trade(quote)
        
        elif (freq == common.QUOTE_TYPE.MIN
              or freq == common.QUOTE_TYPE.FIVEMIN
              or freq == common.QUOTE_TYPE.QUARTERMIN
              or freq == common.QUOTE_TYPE.SPECIALBAR):
            return self._make_trade_bar(quote)
        
        elif freq == common.QUOTE_TYPE.DAILY:
            return self._make_trade_bar(quote)
    
    def _make_trade_bar(self, quote_dic):
        
        result = []
        
        for entrust_no, order in self.orders.items():
            quote = quote_dic[order.symbol]
            low = quote.low
            high = quote.high
            quote_date = quote.trade_date
            quote_time = quote.time
            volume = quote.volume
            
            '''
            if order.order_type == common.ORDER_TYPE.LIMIT:
                if order.entrust_action == common.ORDER_ACTION.BUY and order.entrust_price >= low:
                    trade = Trade()
                    trade.init_from_order(order)
                    trade.send_fill_info(order.entrust_price, order.entrust_size,
                                         quote_date, quote_time,
                                         self._next_fill_no())
                    
                    order.order_status = common.ORDER_STATUS.FILLED
                    order.fill_size = trade.fill_size
                    order.fill_price = trade.fill_price
                    
                    orderstatus_ind = OrderStatusInd()
                    orderstatus_ind.init_from_order(order)
                    
                    result.append((trade, orderstatus_ind))
                    
                elif order.entrust_action == common.ORDER_ACTION.SELL and order.entrust_price <= high:
                    trade = Trade()
                    trade.init_from_order(order)
                    trade.send_fill_info(order.entrust_price, order.entrust_size,
                                         quote_date, quote_time,
                                         self._next_fill_no())
                    
                    order.order_status = common.ORDER_STATUS.FILLED
                    order.fill_size = trade.fill_size
                    order.fill_price = trade.fill_price
                    
                    orderstatus_ind = OrderStatusInd()
                    orderstatus_ind.init_from_order(order)

                    result.append((trade, orderstatus_ind))
            
            elif order.order_type == common.ORDER_TYPE.STOP:
                if order.entrust_action == common.ORDER_ACTION.BUY and order.entrust_price <= high:
                    trade = Trade()
                    trade.init_from_order(order)
                    trade.send_fill_info(order.entrust_price, order.entrust_size,
                                         quote_date, quote_time,
                                         self._next_fill_no())
                    
                    order.order_status = common.ORDER_STATUS.FILLED
                    order.fill_size = trade.fill_size
                    order.fill_price = trade.fill_price
                    orderstatus_ind = OrderStatusInd()
                    orderstatus_ind.init_from_order(order)
                    result.append((trade, orderstatus_ind))
                
                if order.entrust_action == common.ORDER_ACTION.SELL and order.entrust_price >= low:
                    trade = Trade()
                    trade.init_from_order(order)
                    trade.send_fill_info(order.entrust_price, order.entrust_size,
                                         quote_date, quote_time,
                                         self._next_fill_no())
                    
                    order.order_status = common.ORDER_STATUS.FILLED
                    order.fill_size = trade.fill_size
                    order.fill_price = trade.fill_price
                    orderstatus_ind = OrderStatusInd()
                    orderstatus_ind.init_from_order(order)
                    result.append((trade, orderstatus_ind))
            '''
            
            entrust_price = order.entrust_price
            entrust_size = order.entrust_size
            
            fill_size = 0
            if order.order_type == common.ORDER_TYPE.LIMIT:
                if common.ORDER_ACTION.is_positive(order.entrust_action) and entrust_price >= low:
                    fill_price = min(entrust_price, high)
                    # fill_size = min(entrust_size, self.participation_rate * volume)
                    fill_size = entrust_size
                    
                elif common.ORDER_ACTION.is_negative(order.entrust_action) and order.entrust_price <= high:
                    fill_price = max(entrust_price, low)
                    # fill_size = min(entrust_size, self.participation_rate * volume)
                    fill_size = entrust_size

            elif order.order_type == common.ORDER_TYPE.STOP:
                if common.ORDER_ACTION.is_positive(order.entrust_action) and order.entrust_price <= high:
                    fill_price = max(entrust_price, low)
                    # fill_size = min(entrust_size, self.participation_rate * volume)
                    fill_size = entrust_size

                if common.ORDER_ACTION.is_negative(order.entrust_action) and order.entrust_price >= low:
                    fill_price = min(entrust_price, high)
                    # fill_size = min(entrust_size, self.participation_rate * volume)
                    fill_size = entrust_size
            
            elif order.order_type == common.ORDER_TYPE.VWAP:
                fill_price = quote.vwap
                fill_size = entrust_size

            if not fill_size:
                continue
                
            trade_ind = Trade(order)
            trade_ind.set_fill_info(order.entrust_price, order.entrust_size,
                                    quote_date, quote_time,
                                    self._next_fill_no())
            
            order.fill_price = ((order.fill_price * order.fill_size + fill_size * fill_price)
                                / (order.fill_size + fill_size))
            order.fill_size += fill_size
            if order.fill_size == order.entrust_size:
                order.order_status = common.ORDER_STATUS.FILLED
            
            order_status_ind = OrderStatusInd(order)
            
            result.append((trade_ind, order_status_ind))
            
        self.orders = {k: v for k, v in self.orders.items() if not v.is_finished}

        return result
    
    def cancel_order(self, entrust_no):
        order = self.orders.pop(entrust_no)
        order.cancel_size = order.entrust_size - order.fill_size
        order.order_status = common.ORDER_STATUS.CANCELLED
        
        order_status_ind = OrderStatusInd(order)
        
        '''
        for i in xrange(len(self.orders)):
            order = self.orders[i]
            
            if (order.is_finished):
                continue
            
            if (order.entrust_no == entrust_no):
                order.cancel_size = order.entrust_size - order.fill_size
                order.order_status = common.ORDER_STATUS.CANCELLED
            
            # todo
            orderstatus = OrderStatusInd()
            orderstatus.init_from_order(order)
            
            return orderstatus
    
        '''
        return order_status_ind
        
    '''
Exemplo n.º 15
0
class DailyStockSimulator(object):
    """This is not event driven!

    Attributes
    ----------
    __orders : list of Order
        Store orders that have not been filled.

    """
    def __init__(self):
        # TODO heap is better for insertion and deletion. We only need implement search of heapq module.
        self.__orders = dict()
        self.seq_gen = SequenceGenerator()

        self.date = 0
        self.time = 0

    def on_new_day(self, trade_date):
        self.date = trade_date

    def on_after_market_close(self):
        # self._refresh_orders() #TODO sometimes we do not want to refresh (multi-days match)
        pass

    def _refresh_orders(self):
        self.__orders.clear()

    def _next_fill_no(self):
        return str(
            np.int64(self.date) * 10000 + self.seq_gen.get_next('fill_no'))

    @property
    def match_finished(self):
        return len(self.__orders) == 0

    @staticmethod
    def _validate_order(order):
        # TODO to be enhanced
        assert order is not None

    @staticmethod
    def _validate_price(price_dic):
        # TODO to be enhanced
        assert price_dic is not None

    def _get_next_entrust_no(self):
        """used to generate id for orders and trades."""
        return str(self.seq_gen.get_next('entrust_no'))

    def add_order(self, order):
        """
        Add one order to the simulator.

        Parameters
        ----------
        order : Order

        Returns
        -------
        err_msg : str
            default ""

        """
        neworder = copy.copy(order)
        self._validate_order(order)

        entrust_no = self._get_next_entrust_no()
        neworder.entrust_no = entrust_no

        self.__orders[entrust_no] = neworder
        return entrust_no

    def cancel_order(self, entrust_no):
        """
        Cancel an order.

        Parameters
        ----------
        entrust_no : str

        Returns
        -------
        err_msg : str
            default ""

        """
        order = self.__orders.pop(entrust_no, None)
        if order is None:
            err_msg = "No order with entrust_no {} in simulator.".format(
                entrust_no)
            order_status_ind = None
        else:
            order.cancel_size = order.entrust_size - order.fill_size
            order.order_status = common.ORDER_STATUS.CANCELLED

            err_msg = ""
            order_status_ind = OrderStatusInd(order)
        return order_status_ind, err_msg

    def match(self, price_dic, date=19700101, time=150000):
        self._validate_price(price_dic)

        results = []
        for order in self.__orders.values():
            symbol = order.symbol
            symbol_dic = price_dic[symbol]

            # get fill price
            if isinstance(order, FixedPriceTypeOrder):
                price_target = order.price_target
                fill_price = symbol_dic[price_target]
            elif isinstance(order, VwapOrder):
                if order.start != -1:
                    raise NotImplementedError("Vwap of a certain time range")
                fill_price = symbol_dic['vwap']
            elif isinstance(order, Order):
                # TODO
                fill_price = symbol_dic['close']
            else:
                raise NotImplementedError("order class {} not support!".format(
                    order.__class__))

            # get fill size
            fill_size = order.entrust_size - order.fill_size

            # create trade indication
            trade_ind = Trade(order)
            trade_ind.set_fill_info(fill_price, fill_size, date, time,
                                    self._next_fill_no())

            # update order status
            order.fill_price = (order.fill_price * order.fill_size +
                                fill_price * fill_size) / (order.fill_size +
                                                           fill_size)
            order.fill_size += fill_size
            if order.fill_size == order.entrust_size:
                order.order_status = common.ORDER_STATUS.FILLED

            order_status_ind = OrderStatusInd(order)

            results.append((trade_ind, order_status_ind))

        self.__orders = {
            k: v
            for k, v in self.__orders.items() if not v.is_finished
        }
        # self.cancel_order(order.entrust_no)  # TODO DEBUG

        return results
Exemplo n.º 16
0
class Strategy(with_metaclass(abc.ABCMeta)):
    """
    Abstract base class for strategies.

    Attributes
    ----------
    ctx : Context object
        Used to store relevant context of the strategy.
    run_mode : int
        Whether the trategy is under back-testing or live trading.
    pm : trade.PortfolioManger
        Responsible for managing orders, trades and positions.
    store : dict
        A dictionary to store variables that will be automatically saved.

    Methods
    -------

    """
    def __init__(self):
        super(Strategy, self).__init__()
        self.ctx = None
        # self.run_mode = common.RUN_MODE.BACKTEST

        # self.ctx.pm = PortfolioManager(strategy=self)
        # self.pm = self.ctx.pm

        # self.task_id_map = defaultdict(list)
        self.seq_gen = SequenceGenerator()

        self.init_balance = 0.0

    def init_from_config(self, props):
        pass

    def initialize(self):
        pass

    def _get_next_num(self, key):
        """used to generate id for orders and trades."""
        return str(
            np.int64(self.ctx.trade_date) * 10000 + self.seq_gen.get_next(key))

    '''
    # -------------------------------------------------------------------------------------------
    # Order

    def place_order(self, symbol, action, price, size, algo="", algo_param=None):
        """
        Send a request with an order to the system. Execution algorithm will be automatically chosen.
        Returns task_id which can be used to query execution and orders of this task.

        Parameters
        ----------
        symbol : str
            the symbol of symbol to be ordered, eg. "000001.SZ".
        action : str
        price : float.
            The price to be ordered at.
        size : int
            The quantity to be ordered at.
        algo : str, optional
            The algorithm to be used. If None then use default algorithm.
        algo_param : dict, optional
            Parameters of the algorithm. Default {}.

        Returns
        -------
        res : str
        msg : str.
            if res is None, message contains error information

        """
        pass

    def cancel_order(self, task_id):
        """Cancel all uncome orders of a task according to its task ID.

        Parameters
        ----------
        task_id : str
            ID of the task.
            NOTE we CANNOT cancel order by entrust_no because this may break the execution of algorithm.

        Returns
        -------
        result : str
            Indicate whether the cancel succeed.
        err_msg : str

        """
        pass

    # -------------------------------------------------------------------------------------------
    # Query

    def query_account(self):
        """
        
        Returns
        -------
        pd.DataFrame

        """
        pass

    def query_universe(self):
        """
        
        Returns
        -------
        pd.DataFrame

        """
        pass

    def query_position(self, mode="all", symbols=""):
        """
        Parameters
        ----------
        mode : str, optional
        symbols : str, optional
            Separated by ,
        
        Returns
        -------
        pd.DataFrame
        
        """
        pass

    def query_portfolio(self):
        """
        Return net positions of all securities in the strategy universe (including zero positions).

        Returns
        --------
        pd.DataFrame
            Current position of the strategy.

        """
        pass

    def query_task(self, task_id=-1):
        """
        Query order information of current day.

        Parameters
        ----------
        task_id : int, optional
            ID of the task. -1 by default (return all orders of the day; else return orders of this task).

        Returns
        -------
        pd.DataFrame

        """

    def query_order(self, task_id=-1):
        """
        Query order information of current day.

        Parameters
        ----------
        task_id : int
            ID of the task. -1 by default (return all orders of the day; else return orders of this task).

        Returns
        -------
        pd.DataFrame

        """
        pass

    def query_trade(self, task_id=-1):
        """
        Query trade information of current day.

        Parameters
        -----------
        task_id : int
            ID of the task. -1 by default (return all orders of the day; else return orders of this task).

        Returns
        --------
        pd.DataFrame

        """
        pass

    # -------------------------------------------------------------------------------------------
    # Portfolio Order

    def goal_portfolio(self, positions, algo="", algo_param=None):
        """
        Let the system automatically generate orders according to portfolio positions goal.
        If there are uncome orders of any symbol in the strategy universe, this order will be rejected. #TODO not impl

        Parameters
        -----------
        positions : list of GoalPosition
            This must include positions of all securities in the strategy universe.
            Use former value if there is no change.
        algo : str, optional
            The algorithm to be used. If None then use default algorithm.
        algo_param : dict, optional
            Parameters of the algorithm. Default {}.

        Returns
        --------
        result : bool
            Whether this command is accepted. True means the system's acceptance, instead of positions have changed.
        err_msg : str

        """
        pass

    def stop_portfolio(self):
        """
        Returns
        -------
        result : str
        message : str
            If result is None, message contains error information
        
        """
        pass

    def place_batch_order(self, orders, algo="", algo_param=None):
        """Send a batch of orders to the system together.

        Parameters
        -----------
        orders : list
            a list of trade.model.Order objects.
        algo : str, optional
            The algorithm to be used. If None then use default algorithm.
        algo_param : dict, optional
            Parameters of the algorithm. Default {}.

        Returns
        -------
        task_id : str
            Task ID generated by entrust_order.
        err_msg : str.

        """
        pass

    def basket_order(self, orders, algo="", algo_param=None):
        """
        Parameters
        ----------
        orders : list of dict
            [ {"security": "000001.SZ", "ref_price": 10.0, "inc_size" : 100}, ...]
        algo : str, optional
        algo_param : dict or None, optional
        
        Returns
        -------
        result : str
        message : str
            If result is None, message contains error information
        
        """
        pass
    '''

    # -------------------------------------------------------------------------------------------
    # Callback Indications & Responses

    def on_trade(self, ind):
        """

        Parameters
        ----------
        ind : TradeInd

        Returns
        -------

        """
        pass

    def on_order_status(self, ind):
        """

        Parameters
        ----------
        ind : OrderStatusInd

        Returns
        -------

        """
        pass

    def on_order_rsp(self, rsp):
        """
        
        Parameters
        ----------
        rsp

        """
        pass

    def on_task_rsp(self, rsp):
        """
        
        Parameters
        ----------
        rsp

        """
        pass

    def on_task_status(self, ind):
        """
        
        Parameters
        ----------
        rsp

        """
        pass
Exemplo n.º 17
0
class Strategy(with_metaclass(abc.ABCMeta)):
    """
    Abstract base class for strategies.

    Attributes
    ----------
    ctx : Context object
        Used to store relevant context of the strategy.
    run_mode : int
        Whether the trategy is under back-testing or live trading.
    pm : trade.PortfolioManger
        Responsible for managing orders, trades and positions.
    store : dict
        A dictionary to store variables that will be automatically saved.

    Methods
    -------

    """
    
    def __init__(self):
        super(Strategy, self).__init__()
        self.ctx = None
        # self.run_mode = common.RUN_MODE.BACKTEST
        
        # self.ctx.pm = PortfolioManager(strategy=self)
        # self.pm = self.ctx.pm

        # self.task_id_map = defaultdict(list)
        self.seq_gen = SequenceGenerator()

        self.init_balance = 0.0
        
    def init_from_config(self, props):
        pass
    
    def initialize(self):
        pass
    
    def _get_next_num(self, key):
        """used to generate id for orders and trades."""
        return str(np.int64(self.ctx.trade_date) * 10000 + self.seq_gen.get_next(key))
    
    '''
    # -------------------------------------------------------------------------------------------
    # Order

    def place_order(self, symbol, action, price, size, algo="", algo_param=None):
        """
        Send a request with an order to the system. Execution algorithm will be automatically chosen.
        Returns task_id which can be used to query execution and orders of this task.

        Parameters
        ----------
        symbol : str
            the symbol of symbol to be ordered, eg. "000001.SZ".
        action : str
        price : float.
            The price to be ordered at.
        size : int
            The quantity to be ordered at.
        algo : str, optional
            The algorithm to be used. If None then use default algorithm.
        algo_param : dict, optional
            Parameters of the algorithm. Default {}.

        Returns
        -------
        res : str
        msg : str.
            if res is None, message contains error information

        """
        pass

    def cancel_order(self, task_id):
        """Cancel all uncome orders of a task according to its task ID.

        Parameters
        ----------
        task_id : str
            ID of the task.
            NOTE we CANNOT cancel order by entrust_no because this may break the execution of algorithm.

        Returns
        -------
        result : str
            Indicate whether the cancel succeed.
        err_msg : str

        """
        pass

    # -------------------------------------------------------------------------------------------
    # Query

    def query_account(self):
        """
        
        Returns
        -------
        pd.DataFrame

        """
        pass

    def query_universe(self):
        """
        
        Returns
        -------
        pd.DataFrame

        """
        pass

    def query_position(self, mode="all", symbols=""):
        """
        Parameters
        ----------
        mode : str, optional
        symbols : str, optional
            Separated by ,
        
        Returns
        -------
        pd.DataFrame
        
        """
        pass

    def query_portfolio(self):
        """
        Return net positions of all securities in the strategy universe (including zero positions).

        Returns
        --------
        pd.DataFrame
            Current position of the strategy.

        """
        pass

    def query_task(self, task_id=-1):
        """
        Query order information of current day.

        Parameters
        ----------
        task_id : int, optional
            ID of the task. -1 by default (return all orders of the day; else return orders of this task).

        Returns
        -------
        pd.DataFrame

        """

    def query_order(self, task_id=-1):
        """
        Query order information of current day.

        Parameters
        ----------
        task_id : int
            ID of the task. -1 by default (return all orders of the day; else return orders of this task).

        Returns
        -------
        pd.DataFrame

        """
        pass

    def query_trade(self, task_id=-1):
        """
        Query trade information of current day.

        Parameters
        -----------
        task_id : int
            ID of the task. -1 by default (return all orders of the day; else return orders of this task).

        Returns
        --------
        pd.DataFrame

        """
        pass

    # -------------------------------------------------------------------------------------------
    # Portfolio Order

    def goal_portfolio(self, positions, algo="", algo_param=None):
        """
        Let the system automatically generate orders according to portfolio positions goal.
        If there are uncome orders of any symbol in the strategy universe, this order will be rejected. #TODO not impl

        Parameters
        -----------
        positions : list of GoalPosition
            This must include positions of all securities in the strategy universe.
            Use former value if there is no change.
        algo : str, optional
            The algorithm to be used. If None then use default algorithm.
        algo_param : dict, optional
            Parameters of the algorithm. Default {}.

        Returns
        --------
        result : bool
            Whether this command is accepted. True means the system's acceptance, instead of positions have changed.
        err_msg : str

        """
        pass

    def stop_portfolio(self):
        """
        Returns
        -------
        result : str
        message : str
            If result is None, message contains error information
        
        """
        pass

    def place_batch_order(self, orders, algo="", algo_param=None):
        """Send a batch of orders to the system together.

        Parameters
        -----------
        orders : list
            a list of trade.model.Order objects.
        algo : str, optional
            The algorithm to be used. If None then use default algorithm.
        algo_param : dict, optional
            Parameters of the algorithm. Default {}.

        Returns
        -------
        task_id : str
            Task ID generated by entrust_order.
        err_msg : str.

        """
        pass

    def basket_order(self, orders, algo="", algo_param=None):
        """
        Parameters
        ----------
        orders : list of dict
            [ {"security": "000001.SZ", "ref_price": 10.0, "inc_size" : 100}, ...]
        algo : str, optional
        algo_param : dict or None, optional
        
        Returns
        -------
        result : str
        message : str
            If result is None, message contains error information
        
        """
        pass
    '''
    
    # -------------------------------------------------------------------------------------------
    # Callback Indications & Responses
    
    def on_trade(self, ind):
        """

        Parameters
        ----------
        ind : TradeInd

        Returns
        -------

        """
        pass

    def on_order_status(self, ind):
        """

        Parameters
        ----------
        ind : OrderStatusInd

        Returns
        -------

        """
        pass
    
    def on_order_rsp(self, rsp):
        """
        
        Parameters
        ----------
        rsp

        """
        pass

    def on_task_rsp(self, rsp):
        """
        
        Parameters
        ----------
        rsp

        """
        pass

    def on_task_status(self, ind):
        """
        
        Parameters
        ----------
        rsp

        """
        pass
Exemplo n.º 18
0
class DailyStockSimulator(object):
    """This is not event driven!

    Attributes
    ----------
    __orders : list of Order
        Store orders that have not been filled.

    """
    
    def __init__(self):
        # TODO heap is better for insertion and deletion. We only need implement search of heapq module.
        self.__orders = dict()
        self.seq_gen = SequenceGenerator()
        
        self.date = 0
        self.time = 0
    
    def on_new_day(self, trade_date):
        self.date = trade_date
    
    def on_after_market_close(self):
        # self._refresh_orders() #TODO sometimes we do not want to refresh (multi-days match)
        pass
    
    def _refresh_orders(self):
        self.__orders.clear()
    
    def _next_fill_no(self):
        return str(np.int64(self.date) * 10000 + self.seq_gen.get_next('fill_no'))
    
    @property
    def match_finished(self):
        return len(self.__orders) == 0
    
    @staticmethod
    def _validate_order(order):
        # TODO to be enhanced
        assert order is not None
    
    @staticmethod
    def _validate_price(price_dic):
        # TODO to be enhanced
        assert price_dic is not None

    def _get_next_entrust_no(self):
        """used to generate id for orders and trades."""
        return str(self.seq_gen.get_next('entrust_no'))
    
    def add_order(self, order):
        """
        Add one order to the simulator.

        Parameters
        ----------
        order : Order

        Returns
        -------
        err_msg : str
            default ""

        """
        neworder = copy.copy(order)
        self._validate_order(order)

        entrust_no = self._get_next_entrust_no()
        neworder.entrust_no = entrust_no

        self.__orders[entrust_no] = neworder
        return entrust_no
    
    def cancel_order(self, entrust_no):
        """
        Cancel an order.

        Parameters
        ----------
        entrust_no : str

        Returns
        -------
        err_msg : str
            default ""

        """
        order = self.__orders.pop(entrust_no, None)
        if order is None:
            err_msg = "No order with entrust_no {} in simulator.".format(entrust_no)
            order_status_ind = None
        else:
            order.cancel_size = order.entrust_size - order.fill_size
            order.order_status = common.ORDER_STATUS.CANCELLED
            
            err_msg = ""
            order_status_ind = OrderStatusInd(order)
        return order_status_ind, err_msg
    
    def match(self, price_dic, date=19700101, time=150000):
        self._validate_price(price_dic)
        
        results = []
        for order in self.__orders.values():
            symbol = order.symbol
            symbol_dic = price_dic[symbol]
            
            # get fill price
            if isinstance(order, FixedPriceTypeOrder):
                price_target = order.price_target
                fill_price = symbol_dic[price_target]
            elif isinstance(order, VwapOrder):
                if order.start != -1:
                    raise NotImplementedError("Vwap of a certain time range")
                fill_price = symbol_dic['vwap']
            elif isinstance(order, Order):
                # TODO
                fill_price = symbol_dic['close']
            else:
                raise NotImplementedError("order class {} not support!".format(order.__class__))
            
            # get fill size
            fill_size = order.entrust_size - order.fill_size
            
            # create trade indication
            trade_ind = Trade(order)
            trade_ind.set_fill_info(fill_price, fill_size,
                                    date, time,
                                    self._next_fill_no())
            
            # update order status
            order.fill_price = (order.fill_price * order.fill_size
                                + fill_price * fill_size) / (order.fill_size + fill_size)
            order.fill_size += fill_size
            if order.fill_size == order.entrust_size:
                order.order_status = common.ORDER_STATUS.FILLED
                
            order_status_ind = OrderStatusInd(order)
            
            results.append((trade_ind, order_status_ind))
        
        self.__orders = {k: v for k, v in self.__orders.items() if not v.is_finished}
        # self.cancel_order(order.entrust_no)  # TODO DEBUG
        
        return results
Exemplo n.º 19
0
class RealTimeTradeApi_async(BaseTradeApi, EventEngine):
    """
    Attributes
    ----------
    _trade_api : TradeApi
    
    """
    def __init__(self):
        super(RealTimeTradeApi_async, self).__init__()

        self.ctx = None

        self._trade_api = None

        self._task_no_id_map = dict()

        self.seq_gen = SequenceGenerator()

    def init_from_config(self, props):
        """
        Instantiate TradeAPI and login.
        
        Parameters
        ----------
        props : dict

        """
        if self._trade_api is not None:
            self._trade_api.close()

        def get_from_list_of_dict(l, key, default=None):
            res = None
            for dic in l:
                res = dic.get(key, None)
                if res is not None:
                    break
            if res is None:
                res = default
            return res

        props_default = dict(
        )  # jutil.read_json(jutil.join_relative_path('etc/trade_config.json'))
        dic_list = [props, props_default]

        address = get_from_list_of_dict(dic_list, "remote.trade.address", "")
        username = get_from_list_of_dict(dic_list, "remote.trade.username", "")
        password = get_from_list_of_dict(dic_list, "remote.trade.password", "")
        if address is None or username is None or password is None:
            raise ValueError("no address, username or password available!")

        tapi = TradeApi(address)
        self.set_trade_api_callbacks(tapi)

        # 使用用户名、密码登陆, 如果成功,返回用户可用的策略帐号列表
        print("\n{}@{} login...".format(username, address))
        user_info, msg = tapi.login(username, password)
        print("    Login msg: {:s}".format(msg))
        print("    Login user info: {:s}\n".format(user_info))
        self._trade_api = tapi

        # event types and trade_api functions are one-to-one corresponded
        self._omni_api_map = {
            EVENT_TYPE.QUERY_ACCOUNT: self._trade_api.query_account,
            EVENT_TYPE.QUERY_UNIVERSE: self._trade_api.query_universe,
            EVENT_TYPE.QUERY_POSITION: self._trade_api.query_position,
            EVENT_TYPE.QUERY_PORTFOLIO: self._trade_api.query_portfolio,
            EVENT_TYPE.QUERY_TASK: self._trade_api.query_task,
            EVENT_TYPE.QUERY_TRADE: self._trade_api.query_trade,
            EVENT_TYPE.QUERY_ORDER: self._trade_api.query_order,
        }

    # -------------------------------------------------------------------------------------------
    # On TradeAPI Callback: put a corresponding event to EventLiveTradeInstance

    def set_trade_api_callbacks(self, trade_api):
        trade_api.set_task_status_callback(self.on_task_status)
        trade_api.set_order_status_callback(self.on_order_status)
        trade_api.set_trade_callback(self.on_trade)
        trade_api.set_connection_callback(self.on_connection_callback)

    def on_connection_callback(self, connected):
        """
        
        Parameters
        ----------
        connected : bool

        """
        if connected:
            print("TradeAPI connected.")
            event_type = EVENT_TYPE.TRADE_API_CONNECTED
        else:
            print("TradeAPI disconnected.")
            event_type = EVENT_TYPE.TRADE_API_DISCONNECTED
        e = Event(event_type)
        self.ctx.instance.put(e)

    def on_trade(self, ind_dic):
        """
        
        Parameters
        ----------
        ind_dic : dict

        """
        # print("\nGateway on trade: ")
        # print(ind_dic)
        if 'security' in ind_dic:
            ind_dic['symbol'] = ind_dic.pop('security')

        ind = Trade.create_from_dict(ind_dic)
        ind.task_no = self._task_no_id_map[ind.task_id]

        e = Event(EVENT_TYPE.TRADE_IND)
        e.dic['ind'] = ind
        self.ctx.instance.put(e)

    def on_order_status(self, ind_dic):
        """
        
        Parameters
        ----------
        ind_dic : dict

        """
        # print("\nGateway on order status: ")
        # print(ind_dic)
        if 'security' in ind_dic:
            ind_dic['symbol'] = ind_dic.pop('security')

        ind = OrderStatusInd.create_from_dict(ind_dic)
        ind.task_no = self._task_no_id_map[ind.task_id]

        e = Event(EVENT_TYPE.ORDER_STATUS_IND)
        e.dic['ind'] = ind
        self.ctx.instance.put(e)

    def on_task_status(self, ind_dic):
        # print("\nGateway on task ind: ")
        # print(ind_dic)
        ind = TaskInd.create_from_dict(ind_dic)
        ind.task_no = self._task_no_id_map[ind.task_id]

        e = Event(EVENT_TYPE.TASK_STATUS_IND)
        e.dic['ind'] = ind
        self.ctx.instance.put(e)

    def on_omni_call(self, event):
        func = self._omni_api_map.get(event.type_, None)
        if func is None:
            print("{} not recgonized. Ignore.".format(event))
            return

        args = event.dic.get('args', None)
        func(args)

    def on_goal_portfolio(self, event):
        task = event.dic['task']
        positions = task.data

        # TODO: compatibility
        for dic in positions:
            dic['symbol'] = dic.pop('security')

        task_id, msg = self._trade_api.goal_portfolio(
            positions, algo=task.algo, algo_param=task.algo_param)

        self._generate_on_task_rsp(task.task_no, task_id, msg)

    def _generate_on_task_rsp(self, task_no, task_id, msg):
        # this rsp is generated by gateway itself
        rsp = TaskRsp(task_no=task_no, task_id=task_id, msg=msg)
        if rsp.success:
            self._task_no_id_map[task_id] = task_no

        # DEBUG
        print("\nGateway generate task_rsp {}".format(rsp))
        e = Event(EVENT_TYPE.TASK_RSP)
        e.dic['rsp'] = rsp
        self.ctx.instance.put(e)

    def on_place_order(self, event):
        task = event.dic['task']
        order = task.data
        task_id, msg = self._trade_api.place_order(order.symbol,
                                                   order.entrust_action,
                                                   order.entrust_price,
                                                   order.entrust_size,
                                                   task.algo, task.algo_param)

        self._generate_on_task_rsp(task.task_no, task_id, msg)

    # -------------------------------------------------------------------------------------------
    # Run

    def run(self):
        """
        Listen to certain events and run the EventEngine.
        Events include:
            1. placement & cancellation of orders
            2. query of universe, account, position and portfolio
            3. etc.

        """
        for e_type in self._omni_api_map.keys():
            self.register(e_type, self.on_omni_call)

        self.register(EVENT_TYPE.PLACE_ORDER, self.on_place_order)
        self.register(EVENT_TYPE.CANCEL_ORDER, self.on_omni_call)
        self.register(EVENT_TYPE.GOAL_PORTFOLIO, self.on_goal_portfolio)

        self.start(timer=False)

    # -------------------------------------------------------------------------------------------
    # API

    def _get_next_num(self, key):
        """used to generate id for orders and trades."""
        return str(
            np.int64(self.ctx.trade_date) * 10000 + self.seq_gen.get_next(key))

    def _get_next_task_no(self):
        return self._get_next_num('task_no')

    def publish_event(self, event):
        self.put(event)

    # ----------------------------------------------------------------------------------------
    # place & cancel

    def place_order(self,
                    symbol,
                    action,
                    price,
                    size,
                    algo="",
                    algo_param=None,
                    userdata=""):
        if algo_param is None:
            algo_param = dict()

        # this order object is not for TradeApi, but for strategy itself to remember the order
        order = Order.new_order(symbol, action, price, size,
                                self.ctx.trade_date, 0)
        order.entrust_no = self._get_next_num('entrust_no')

        task = Task(self._get_next_task_no(),
                    algo=algo,
                    algo_param=algo_param,
                    data=order,
                    function_name="place_order")
        self.ctx.pm.add_task(task)
        # self.task_id_map[order.task_id].append(order.entrust_no)

        # self.pm.add_order(order)

        e = Event(EVENT_TYPE.PLACE_ORDER)
        e.dic['task'] = task
        self.publish_event(e)

    def cancel_order(self, entrust_no):
        e = Event(EVENT_TYPE.CANCEL_ORDER)
        e.dic['entrust_no'] = entrust_no
        self.publish_event(e)

    # ----------------------------------------------------------------------------------------
    # PMS

    def goal_portfolio(self, positions, algo="", algo_param=None, userdata=""):
        if algo_param is None:
            algo_param = dict()

        task = Task(self._get_next_task_no(),
                    data=positions,
                    algo=algo,
                    algo_param=algo_param,
                    function_name="goal_portfolio")
        self.ctx.pm.add_task(task)

        e = Event(EVENT_TYPE.GOAL_PORTFOLIO)
        e.dic['task'] = task
        self.publish_event(e)

    # ----------------------------------------------------------------------------------------
    # query account, universe, position, portfolio

    def query_account_async(self, userdata=""):
        args = locals()
        e = Event(EVENT_TYPE.QUERY_ACCOUNT)
        e.dic['args'] = args
        self.publish_event(e)

    def query_universe_async(self, userdata=""):
        args = locals()
        e = Event(EVENT_TYPE.QUERY_UNIVERSE)
        e.dic['args'] = args
        self.publish_event(e)

    def query_position_async(self, mode="all", symbols="", userdata=""):
        args = locals()
        e = Event(EVENT_TYPE.QUERY_POSITION)
        e.dic['args'] = args
        e.dic['securities'] = e.dic.pop['symbols']
        self.publish_event(e)

    def query_portfolio_async(self, userdata=""):
        args = locals()
        e = Event(EVENT_TYPE.QUERY_PORTFOLIO)
        e.dic['args'] = args
        self.publish_event(e)

    def query_account(self, format=""):
        return self._trade_api.query_account(format=format)

    def query_universe(self, format=""):
        return self._trade_api.query_universe(format=format)

    def query_position(self, mode="all", symbols="", format=""):
        return self._trade_api.query_position(mode=mode,
                                              securities=symbols,
                                              format=format)

    def query_portfolio(self, format=""):
        return self._trade_api.query_portfolio(format=format)

    # ----------------------------------------------------------------------------------------
    # Use Strategy

    def use_strategy(self, strategy_id):
        return self._trade_api.use_strategy(strategy_id)

    # ----------------------------------------------------------------------------------------
    # query task, order, trade

    def query_task(self, task_id=-1, userdata=""):
        args = locals()
        e = Event(EVENT_TYPE.QUERY_TASK)
        e.dic['args'] = args
        self.publish_event(e)

    def query_order(self, task_id=-1, userdata=""):
        args = locals()
        e = Event(EVENT_TYPE.QUERY_ORDER)
        e.dic['args'] = args
        self.publish_event(e)

    def query_trade(self, task_id=-1, userdata=""):
        args = locals()
        e = Event(EVENT_TYPE.QUERY_TRADE)
        e.dic['args'] = args
        self.publish_event(e)
Exemplo n.º 20
0
 def __init__(self):
     self.orders = dict()
     
     self.seq_gen = SequenceGenerator()
     self.participation_rate = 1.0
Exemplo n.º 21
0
class AlphaTradeApi(BaseTradeApi):
    def __init__(self):
        super(AlphaTradeApi, self).__init__()
        self.ctx = None

        self._simulator = DailyStockSimulator()
        self.entrust_no_task_id_map = dict()
        self.seq_gen = SequenceGenerator()

        self.commission_rate = 0.0

        self.MATCH_TIME = 143000

    def _get_next_task_id(self):
        return np.int64(
            self.ctx.trade_date) * 10000 + self.seq_gen.get_next('task_id')

    def _add_task_id(self, ind):
        no = ind.entrust_no
        task_id = self.entrust_no_task_id_map[no]
        ind.task_id = task_id
        # ind.task_no = task_id

    def init_from_config(self, props):
        self.commission_rate = props.get('commission_rate', 0.0)

        self.set_order_status_callback(
            lambda ind: self.ctx.strategy.on_order_status(ind))
        self.set_trade_callback(lambda ind: self.ctx.strategy.on_trade(ind))
        self.set_task_status_callback(
            lambda ind: self.ctx.strategy.on_task_status(ind))

    def query_account(self, format=""):
        pass

    def on_new_day(self, trade_date):
        self._simulator.on_new_day(trade_date)

    def on_after_market_close(self):
        self._simulator.on_after_market_close()

    def place_order(self,
                    security,
                    action,
                    price,
                    size,
                    algo="",
                    algo_param={},
                    userdata=""):
        if size <= 0:
            print("Invalid size {}".format(size))
            return

        # Generate Task
        order = Order.new_order(security,
                                action,
                                price,
                                size,
                                self.ctx.trade_date,
                                self.ctx.time,
                                order_type=common.ORDER_TYPE.LIMIT)

        task_id = self._get_next_task_id()
        order.task_id = task_id

        task = Task(task_id,
                    algo=algo,
                    algo_param=algo_param,
                    data=order,
                    function_name='place_order',
                    trade_date=self.ctx.trade_date)
        # task.task_no = task_id

        # Send Order to Exchange
        entrust_no = self._simulator.add_order(order)
        task.data.entrust_no = entrust_no

        self.ctx.pm.add_task(task)
        self.entrust_no_task_id_map[entrust_no] = task.task_id

        order_status_ind = OrderStatusInd(order)
        order_status_ind.order_status = common.ORDER_STATUS.ACCEPTED
        self._order_status_callback(order_status_ind)

        return task_id, ""

    def cancel_order(self, task_id):
        task = self.ctx.pm.get_task(task_id)
        if task.function_name == 'place_order':
            order = task.data
            entrust_no = order.entrust_no
            order_status_ind, err_msg = self._simulator.cancel_order(
                entrust_no)
            task_id = self.entrust_no_task_id_map[entrust_no]
            order_status_ind.task_id = task_id
            # order_status_ind.task_no = task_id
            self._order_status_callback(order_status_ind)
        else:
            raise NotImplementedError(
                "cancel task with function_name = {}".format(
                    task.function_name))

    def goal_portfolio(self, positions, algo="", algo_param={}, userdata=""):
        # Generate Orders
        task_id = self._get_next_task_id()

        orders = []
        for goal in positions:
            sec, goal_size = goal['symbol'], goal['size']
            if sec in self.ctx.pm.holding_securities:
                current_size = self.ctx.pm.get_position(sec).current_size
            else:
                current_size = 0
            diff_size = goal_size - current_size
            if diff_size != 0:
                action = common.ORDER_ACTION.BUY if diff_size > 0 else common.ORDER_ACTION.SELL

                order = FixedPriceTypeOrder.new_order(sec, action, 0.0,
                                                      abs(diff_size),
                                                      self.ctx.trade_date, 0)
                if algo == 'vwap':
                    order.price_target = 'vwap'  # TODO
                elif algo == '':
                    order.price_target = 'vwap'
                else:
                    raise NotImplementedError(
                        "goal_portfolio algo = {}".format(algo))

                order.task_id = task_id
                orders.append(order)

        # Generate Task
        task = Task(task_id,
                    algo=algo,
                    algo_param=algo_param,
                    data=orders,
                    function_name='goal_portfolio',
                    trade_date=self.ctx.trade_date)

        self.ctx.pm.add_task(task)

        # Send Orders to Exchange
        for order in orders:
            entrust_no = self._simulator.add_order(order)
            # task.data.entrust_no = entrust_no
            self.entrust_no_task_id_map[entrust_no] = task.task_id

            order_status_ind = OrderStatusInd(order)
            order_status_ind.order_status = common.ORDER_STATUS.ACCEPTED
            self._order_status_callback(order_status_ind)

    def goal_portfolio_by_batch_order(self, goals):
        assert len(goals) == len(self.ctx.universe)

        orders = []
        for goal in goals:
            sec, goal_size = goal.symbol, goal.size
            if sec in self.ctx.pm.holding_securities:
                current_size = self.ctx.pm.get_position(sec).current_size
            else:
                current_size = 0
            diff_size = goal_size - current_size
            if diff_size != 0:
                action = common.ORDER_ACTION.BUY if diff_size > 0 else common.ORDER_ACTION.SELL

                order = FixedPriceTypeOrder.new_order(sec, action, 0.0,
                                                      abs(diff_size),
                                                      self.ctx.trade_date, 0)
                order.price_target = 'vwap'  # TODO

                orders.append(order)

        for order in orders:
            self._simulator.add_order(order)

    @property
    def match_finished(self):
        return self._simulator.match_finished

    '''
    @abstractmethod
    def match(self, price_dict, time=0):
        """
        Match un-fill orders in simulator. Return trade indications.

        Parameters
        ----------
        price_dict : dict
        time : int
        # TODO: do we need time parameter?

        Returns
        -------
        list

        """
        return self._simulator.match(price_dict, date=self.ctx.trade_date, time=time)

    '''

    def _add_commission(self, ind):
        comm = calc_commission(ind, self.commission_rate)
        ind.commission = comm

    def match_and_callback(self, price_dict):
        results = self._simulator.match(price_dict,
                                        date=self.ctx.trade_date,
                                        time=self.MATCH_TIME)

        for trade_ind, order_status_ind in results:
            self._add_commission(trade_ind)

            task_id = self.entrust_no_task_id_map[trade_ind.entrust_no]
            self._add_task_id(trade_ind)
            self._add_task_id(order_status_ind)

            self._order_status_callback(order_status_ind)
            self._trade_callback(trade_ind)

            task = self.ctx.pm.get_task(task_id)
            if task.is_finished:
                task_ind = TaskInd(task_id,
                                   task_status=task.task_status,
                                   task_algo='',
                                   task_msg="")
                self._task_status_callback(task_ind)
        return results
Exemplo n.º 22
0
class AlphaTradeApi(BaseTradeApi):
    def __init__(self):
        super(AlphaTradeApi, self).__init__()
        self.ctx = None
        
        self._simulator = DailyStockSimulator()
        self.entrust_no_task_id_map = dict()
        self.seq_gen = SequenceGenerator()

        self.commission_rate = 0.0
        
        self.MATCH_TIME = 143000

    def _get_next_task_id(self):
        return np.int64(self.ctx.trade_date) * 10000 + self.seq_gen.get_next('task_id')

    def _add_task_id(self, ind):
        no = ind.entrust_no
        task_id = self.entrust_no_task_id_map[no]
        ind.task_id = task_id
        # ind.task_no = task_id

    def init_from_config(self, props):
        self.commission_rate = props.get('commission_rate', 0.0)
        
        self.set_order_status_callback(lambda ind: self.ctx.strategy.on_order_status(ind))
        self.set_trade_callback(lambda ind: self.ctx.strategy.on_trade(ind))
        self.set_task_status_callback(lambda ind: self.ctx.strategy.on_task_status(ind))
        
    def query_account(self, format=""):
        pass
    
    def on_new_day(self, trade_date):
        self._simulator.on_new_day(trade_date)

    def on_after_market_close(self):
        self._simulator.on_after_market_close()

    def place_order(self, security, action, price, size, algo="", algo_param={}, userdata=""):
        if size <= 0:
            print("Invalid size {}".format(size))
            return
    
        # Generate Task
        order = Order.new_order(security, action, price, size, self.ctx.trade_date, self.ctx.time,
                                order_type=common.ORDER_TYPE.LIMIT)
    
        task_id = self._get_next_task_id()
        order.task_id = task_id
    
        task = Task(task_id,
                    algo=algo, algo_param=algo_param, data=order,
                    function_name='place_order', trade_date=self.ctx.trade_date)
        # task.task_no = task_id
    
        # Send Order to Exchange
        entrust_no = self._simulator.add_order(order)
        task.data.entrust_no = entrust_no
    
        self.ctx.pm.add_task(task)
        self.entrust_no_task_id_map[entrust_no] = task.task_id
    
        order_status_ind = OrderStatusInd(order)
        order_status_ind.order_status = common.ORDER_STATUS.ACCEPTED
        self._order_status_callback(order_status_ind)
    
        return task_id, ""

    def cancel_order(self, task_id):
        task = self.ctx.pm.get_task(task_id)
        if task.function_name == 'place_order':
            order = task.data
            entrust_no = order.entrust_no
            order_status_ind, err_msg = self._simulator.cancel_order(entrust_no)
            task_id = self.entrust_no_task_id_map[entrust_no]
            order_status_ind.task_id = task_id
            # order_status_ind.task_no = task_id
            self._order_status_callback(order_status_ind)
        else:
            raise NotImplementedError("cancel task with function_name = {}".format(task.function_name))

    def goal_portfolio(self, positions, algo="", algo_param={}, userdata=""):
        # Generate Orders
        task_id = self._get_next_task_id()
        
        orders = []
        for goal in positions:
            sec, goal_size = goal['symbol'], goal['size']
            if sec in self.ctx.pm.holding_securities:
                current_size = self.ctx.pm.get_position(sec).current_size
            else:
                current_size = 0
            diff_size = goal_size - current_size
            if diff_size != 0:
                action = common.ORDER_ACTION.BUY if diff_size > 0 else common.ORDER_ACTION.SELL
        
                order = FixedPriceTypeOrder.new_order(sec, action, 0.0, abs(diff_size), self.ctx.trade_date, 0)
                if algo == 'vwap':
                    order.price_target = 'vwap'  # TODO
                elif algo == '':
                    order.price_target = 'vwap'
                else:
                    raise NotImplementedError("goal_portfolio algo = {}".format(algo))

                order.task_id = task_id
                orders.append(order)

        # Generate Task
        task = Task(task_id,
                    algo=algo, algo_param=algo_param, data=orders,
                    function_name='goal_portfolio', trade_date=self.ctx.trade_date)

        self.ctx.pm.add_task(task)

        # Send Orders to Exchange
        for order in orders:
            entrust_no = self._simulator.add_order(order)
            # task.data.entrust_no = entrust_no
            self.entrust_no_task_id_map[entrust_no] = task.task_id
            
            order_status_ind = OrderStatusInd(order)
            order_status_ind.order_status = common.ORDER_STATUS.ACCEPTED
            self._order_status_callback(order_status_ind)
    
    def goal_portfolio_by_batch_order(self, goals):
        assert len(goals) == len(self.ctx.universe)
    
        orders = []
        for goal in goals:
            sec, goal_size = goal.symbol, goal.size
            if sec in self.ctx.pm.holding_securities:
                current_size = self.ctx.pm.get_position(sec).current_size
            else:
                current_size = 0
            diff_size = goal_size - current_size
            if diff_size != 0:
                action = common.ORDER_ACTION.BUY if diff_size > 0 else common.ORDER_ACTION.SELL
            
                order = FixedPriceTypeOrder.new_order(sec, action, 0.0, abs(diff_size), self.ctx.trade_date, 0)
                order.price_target = 'vwap'  # TODO
            
                orders.append(order)
        
        for order in orders:
            self._simulator.add_order(order)

    @property
    def match_finished(self):
        return self._simulator.match_finished
    
    '''
    @abstractmethod
    def match(self, price_dict, time=0):
        """
        Match un-fill orders in simulator. Return trade indications.

        Parameters
        ----------
        price_dict : dict
        time : int
        # TODO: do we need time parameter?

        Returns
        -------
        list

        """
        return self._simulator.match(price_dict, date=self.ctx.trade_date, time=time)

    '''
    def _add_commission(self, ind):
        comm = calc_commission(ind, self.commission_rate)
        ind.commission = comm
        
    def match_and_callback(self, price_dict):
        results = self._simulator.match(price_dict, date=self.ctx.trade_date, time=self.MATCH_TIME)
    
        for trade_ind, order_status_ind in results:
            self._add_commission(trade_ind)
        
            task_id = self.entrust_no_task_id_map[trade_ind.entrust_no]
            self._add_task_id(trade_ind)
            self._add_task_id(order_status_ind)
        
            self._order_status_callback(order_status_ind)
            self._trade_callback(trade_ind)
        
            task = self.ctx.pm.get_task(task_id)
            if task.is_finished:
                task_ind = TaskInd(task_id, task_status=task.task_status,
                                   task_algo='', task_msg="")
                self._task_status_callback(task_ind)
        return results
Exemplo n.º 23
0
    def __init__(self):
        self.orders = []
        self.trade_id = 0
        self.order_id = 0

        self.seq_gen = SequenceGenerator()
Exemplo n.º 24
0
class StockSimulatorDaily(object):
    """This is not event driven!

    Attributes
    ----------
    __orders : list of Order
        Store orders that have not been filled.
    __order_id : int
        Current order id

    """
    def __init__(self):
        # TODO heap is better for insertion and deletion. We only need implement search of heapq module.
        self.__orders = dict()
        self.seq_gen = SequenceGenerator()

        self.date = 0
        self.time = 0

    def on_new_day(self, trade_date):
        self.date = trade_date
        self.time = 150000
        # self._refresh_orders() #TODO sometimes we do not want to refresh (multi-days match)

    def _refresh_orders(self):
        self.__orders.clear()

    def _next_fill_no(self):
        return str(
            np.int64(self.date) * 10000 + self.seq_gen.get_next('fill_no'))

    @property
    def match_finished(self):
        return len(self.__orders) == 0

    @staticmethod
    def _validate_order(order):
        # TODO to be enhanced
        assert order is not None

    @staticmethod
    def _validate_price(price_dic):
        # TODO to be enhanced
        assert price_dic is not None

    def add_order(self, order):
        """
        Add one order to the simulator.

        Parameters
        ----------
        order : Order

        Returns
        -------
        err_msg : str
            default ""

        """
        self._validate_order(order)

        if order.entrust_no in self.__orders:
            err_msg = "order with entrust_no {} already exists in simulator".format(
                order.entrust_no)
        self.__orders[order.entrust_no] = order
        err_msg = ""
        return err_msg

    def cancel_order(self, entrust_no):
        """
        Cancel an order.

        Parameters
        ----------
        entrust_no : str

        Returns
        -------
        err_msg : str
            default ""

        """
        popped = self.__orders.pop(entrust_no, None)
        if popped is None:
            err_msg = "No order with entrust_no {} in simulator.".format(
                entrust_no)
            order_status_ind = None
        else:
            err_msg = ""
            order_status_ind = OrderStatusInd()
            order_status_ind.init_from_order(popped)
            order_status_ind.order_status = common.ORDER_STATUS.CANCELLED
        return order_status_ind, err_msg

    def match(self, price_dic, date=19700101, time=150000):
        self._validate_price(price_dic)

        results = []
        for order in self.__orders.values():  # TODO viewvalues()
            symbol = order.symbol
            df = price_dic[symbol]
            if 'vwap' not in df.columns:
                # df.loc[:, 'vwap'] = df.loc[:, 'turnover'] / df.loc[:, 'volume']
                pass

            # get fill price
            if isinstance(order, FixedPriceTypeOrder):
                price_target = order.price_target
                fill_price = df.loc[:, price_target].values[0]
            elif isinstance(order, VwapOrder):
                if order.start != -1:
                    raise NotImplementedError("Vwap of a certain time range")
                fill_price = df.loc[:, 'vwap'].values[0]
            elif isinstance(order, Order):
                # TODO
                fill_price = df.loc[:, 'close'].values[0]
            else:
                raise NotImplementedError("order class {} not support!".format(
                    order.__class__))

            # get fill size
            fill_size = order.entrust_size - order.fill_size

            # create trade indication
            trade_ind = Trade()
            trade_ind.init_from_order(order)
            trade_ind.send_fill_info(fill_price, fill_size, date, time,
                                     self._next_fill_no())
            results.append(trade_ind)

            # update order status
            order.fill_price = (order.fill_price * order.fill_size +
                                fill_price * fill_size) / (order.fill_size +
                                                           fill_size)
            order.fill_size += fill_size
            if order.fill_size == order.entrust_size:
                order.order_status = common.ORDER_STATUS.FILLED

        self.__orders = {
            k: v
            for k, v in self.__orders.viewitems() if not v.is_finished
        }
        # self.cancel_order(order.entrust_no)  # TODO DEBUG

        return results
Exemplo n.º 25
0
class Strategy(with_metaclass(abc.ABCMeta)):
    """
    Abstract base class for strategies.

    Attributes
    ----------
    context : Context object
        Used to store relevant context of the strategy.
    run_mode : int
        Whether the strategy is under back-testing or live trading.
    trade_date : int
        current trading date (may be inconsistent with calendar date).
    pm : trade.PortfolioManger
        Responsible for managing orders, trades and positions.

    Methods
    -------

    """
    def __init__(self):
        self.context = None
        self.run_mode = common.RUN_MODE.BACKTEST

        self.pm = PortfolioManager(self)

        self.task_id_map = defaultdict(list)
        self.seq_gen = SequenceGenerator()

        self.trade_date = 0

        self.init_balance = 0.0

    @abc.abstractmethod
    def init_from_config(self, props):
        pass

    def initialize(self, run_mode):
        self.run_mode = run_mode
        # self.register_callback()
        pass

    """
    def register_callback(self):
        gw = self.context.gateway
        gw.register_callback('portfolio manager', self.pm)
        gw.register_callback('on_trade_ind', self.on_trade_ind)
        gw.register_callback('on_order_status', self.on_trade_ind)
    
    """

    def on_new_day(self, trade_date):
        last_date = self.trade_date
        self.trade_date = trade_date
        self.pm.on_new_day(self.trade_date, last_date)

    def _get_next_num(self, key):
        """used to generate id for orders and trades."""
        return str(
            np.int64(self.trade_date) * 10000 + self.seq_gen.get_next(key))

    def place_order(self,
                    symbol,
                    action,
                    price,
                    size,
                    algo="",
                    algo_param=None):
        """
        Send a request with an order to the system. Execution algorithm will be automatically chosen.
        Returns task_id which can be used to query execution and orders of this task.

        Parameters
        ----------
        symbol : str
            the symbol of symbol to be ordered, eg. "000001.SZ".
        action : str
        price : float.
            The price to be ordered at.
        size : int
            The quantity to be ordered at.
        algo : str
            The algorithm to be used. If None then use default algorithm.
        algo_param : dict
            Parameters of the algorithm. Default {}.

        Returns
        -------
        task_id : str
            Task ID generated by entrust_order.
        err_msg : str.

        """
        if algo:
            raise NotImplementedError("algo {}".format(algo))

        order = Order.new_order(symbol, action, price, size, self.trade_date,
                                0)
        order.task_id = self._get_next_num('task_id')
        order.entrust_no = self._get_next_num('entrust_no')

        self.task_id_map[order.task_id].append(order.entrust_no)

        self.pm.add_order(order)

        err_msg = self.context.gateway.place_order(order)

        if err_msg:
            return '0', err_msg
        else:
            return order.task_id, err_msg

    def cancel_order(self, task_id):
        """Cancel all uncome orders of a task according to its task ID.

        Parameters
        ----------
        task_id : str
            ID of the task.
            NOTE we CANNOT cancel order by entrust_no because this may break the execution of algorithm.

        Returns
        -------
        result : str
            Indicate whether the cancel succeed.
        err_msg : str

        """
        entrust_no_list = self.task_id_map.get(task_id, None)
        if entrust_no_list is None:
            return False, "No task id {}".format(task_id)

        err_msgs = []
        for entrust_no in entrust_no_list:
            err_msg = self.context.gateway.cancel_order(entrust_no)
            err_msgs.append(err_msg)
        if any(map(lambda s: bool(s), err_msgs)):
            return False, ','.join(err_msgs)
        else:
            return True, ""

    def place_batch_order(self, orders, algo="", algo_param=None):
        """Send a batch of orders to the system together.

        Parameters
        -----------
        orders : list
            a list of trade.model.Order objects.
        algo : str
            The algorithm to be used. If None then use default algorithm.
        algo_param : dict
            Parameters of the algorithm. Default {}.

        Returns
        -------
        task_id : str
            Task ID generated by entrust_order.
        err_msg : str.

        """
        task_id = self._get_next_num('task_id')
        err_msgs = []
        for order in orders:
            # only add task_id and entrust_no, leave other attributes unchanged.
            order.task_id = task_id
            order.entrust_no = self._get_next_num('entrust_no')

            self.pm.add_order(order)

            err_msg = self.context.gateway.place_order(order)
            err_msgs.append(err_msg)

            self.task_id_map[order.task_id].append(order.entrust_no)

        return task_id, ','.join(err_msgs)

    def query_portfolio(self):
        """
        Return net positions of all securities in the strategy universe (including zero positions).

        Returns
        --------
        positions : list of trade.model.Position}
            Current position of the strategy.
        err_msg : str

        """
        pass

    def goal_portfolio(self, goals):
        """
        Let the system automatically generate orders according to portfolio positions goal.
        If there are uncome orders of any symbol in the strategy universe, this order will be rejected. #TODO not impl

        Parameters
        -----------
        goals : list of GoalPosition
            This must include positions of all securities in the strategy universe.
            Use former value if there is no change.

        Returns
        --------
        result : bool
            Whether this command is accepted. True means the system's acceptance, instead of positions have changed.
        err_msg : str

        """
        assert len(goals) == len(self.context.universe)

        orders = []
        for goal in goals:
            sec, goal_size = goal.symbol, goal.size
            if sec in self.pm.holding_securities:
                curr_size = self.pm.get_position(sec,
                                                 self.trade_date).curr_size
            else:
                curr_size = 0
            diff_size = goal_size - curr_size
            if diff_size != 0:
                action = common.ORDER_ACTION.BUY if diff_size > 0 else common.ORDER_ACTION.SELL

                order = FixedPriceTypeOrder.new_order(sec, action, 0.0,
                                                      abs(diff_size),
                                                      self.trade_date, 0)
                order.price_target = 'vwap'  # TODO

                orders.append(order)
        self.place_batch_order(orders)

    def query_order(self, task_id):
        """
        Query order information of current day.

        Parameters
        ----------
        task_id : str
            ID of the task. if None, return all orders of the day; else return orders of this task.

        Returns
        -------
        orders : list of trade.model.Order objects.
        err_msg : str.

        """
        pass

    def query_trade(self, task_id):
        """
        Query trade information of current day.

        Parameters
        -----------
        task_id : int
            ID of the task. if None, return all trades of the day; else return trades of this task.

        Returns
        --------
        trades : list of trade.model.Trade objects.
        err_msg : str.

        """
        pass

    def on_trade_ind(self, ind):
        """

        Parameters
        ----------
        ind : TradeInd

        Returns
        -------

        """
        self.pm.on_trade_ind(ind)

    def on_order_status(self, ind):
        """

        Parameters
        ----------
        ind : OrderStatusInd

        Returns
        -------

        """
        self.pm.on_order_status(ind)
Exemplo n.º 26
0
class OrderBook(object):
    def __init__(self):
        self.orders = []
        self.trade_id = 0
        self.order_id = 0

        self.seq_gen = SequenceGenerator()

    def next_trade_id(self):
        return self.seq_gen.get_next('trade_id')

    def next_order_id(self):
        return self.seq_gen.get_next('order_id')

    def add_order(self, order):
        neworder = Order()
        # to do
        order.entrust_no = self.next_order_id()
        neworder.copy(order)
        self.orders.append(neworder)

    def make_trade(self, quote, freq='1m'):

        if freq == common.QUOTE_TYPE.TICK:
            # TODO
            return self.makeTickTrade(quote)

        if (freq == common.QUOTE_TYPE.MIN or freq == common.QUOTE_TYPE.FIVEMIN
                or freq == common.QUOTE_TYPE.QUARTERMIN
                or freq == common.QUOTE_TYPE.SPECIALBAR):

            return self.make_trade_bar(quote)

        if freq == common.QUOTE_TYPE.DAILY:
            # TODO
            return self.makeDaiylTrade(quote)

    def make_trade_bar(self, quote):
        # low = quote.loc[:, 'low'].values[0]
        # high = quote.loc[:, 'high'].values[0]
        # quote_time = quote.loc[:, 'time'].values[0]
        # quote_symbol = quote.loc[:, 'symbol'].values[0]
        low = quote.low
        high = quote.high
        quote_time = quote.time
        quote_symbol = quote.symbol

        result = []
        # to be optimized
        for i in xrange(len(self.orders)):
            order = self.orders[i]
            if quote_symbol != order.symbol:
                continue
            if order.is_finished:
                continue

            if order.order_type == common.ORDER_TYPE.LIMIT:
                if order.entrust_action == common.ORDER_ACTION.BUY and order.entrust_price >= low:
                    trade = Trade()
                    trade.fill_no = self.next_trade_id()
                    trade.entrust_no = order.entrust_no
                    trade.symbol = order.symbol
                    trade.entrust_action = order.entrust_action
                    trade.fill_size = order.entrust_size
                    trade.fill_price = order.entrust_price
                    trade.fill_date = order.entrust_date
                    trade.fill_time = quote_time

                    order.order_status = common.ORDER_STATUS.FILLED
                    order.fill_size = trade.fill_size
                    order.fill_price = trade.fill_price

                    orderstatusInd = OrderStatusInd()
                    orderstatusInd.init_from_order(order)
                    result.append((trade, orderstatusInd))

                if order.entrust_action == common.ORDER_ACTION.SELL and order.entrust_price <= high:
                    trade = Trade()
                    trade.fill_no = self.next_trade_id()
                    trade.entrust_no = order.entrust_no
                    trade.symbol = order.symbol
                    trade.entrust_action = order.entrust_action
                    trade.fill_size = order.entrust_size
                    trade.fill_price = order.entrust_price
                    trade.fill_date = order.entrust_date
                    trade.fill_time = quote_time

                    order.order_status = common.ORDER_STATUS.FILLED
                    order.fill_size = trade.fill_size
                    order.fill_price = trade.fill_price

                    orderstatusInd = OrderStatusInd()
                    orderstatusInd.init_from_order(order)
                    result.append((trade, orderstatusInd))

            if order.order_type == common.ORDER_TYPE.STOP:
                if order.entrust_action == common.ORDER_ACTION.BUY and order.entrust_price <= high:
                    trade = Trade()
                    trade.fill_no = self.next_trade_id()
                    trade.entrust_no = order.entrust_no
                    trade.symbol = order.symbol
                    trade.entrust_action = order.entrust_action
                    trade.fill_size = order.entrust_size
                    trade.fill_price = order.entrust_price
                    trade.fill_date = order.entrust_date
                    trade.fill_time = quote_time

                    order.order_status = common.ORDER_STATUS.FILLED
                    order.fill_size = trade.fill_size
                    order.fill_price = trade.fill_price
                    orderstatusInd = OrderStatusInd()
                    orderstatusInd.init_from_order(order)
                    result.append((trade, orderstatusInd))

                if order.entrust_action == common.ORDER_ACTION.SELL and order.entrust_price >= low:
                    trade = Trade()
                    trade.fill_no = self.next_trade_id()
                    trade.entrust_no = order.entrust_no
                    trade.symbol = order.symbol
                    trade.entrust_action = order.entrust_action
                    trade.fill_size = order.entrust_size
                    trade.fill_price = order.entrust_price
                    trade.fill_date = order.entrust_date
                    trade.fill_time = quote_time

                    order.order_status = common.ORDER_STATUS.FILLED
                    order.fill_size = trade.fill_size
                    order.fill_price = trade.fill_price
                    orderstatusInd = OrderStatusInd()
                    orderstatusInd.init_from_order(order)
                    result.append((trade, orderstatusInd))

        return result

    def cancel_order(self, entrust_no):
        for i in xrange(len(self.orders)):
            order = self.orders[i]

            if (order.is_finished):
                continue

            if (order.entrust_no == entrust_no):
                order.cancel_size = order.entrust_size - order.fill_size
                order.order_status = common.ORDER_STATUS.CANCELLED

            # todo
            orderstatus = OrderStatusInd()
            orderstatus.init_from_order(order)

            return orderstatus

    def cancel_all(self):
        result = []
        for order in self.orders:
            if order.is_finished:
                continue
            order.cancel_size = order.entrust_size - order.fill_size
            order.order_status = common.ORDER_STATUS.CANCELLED

            # todo
            orderstatus = OrderStatusInd()
            orderstatus.init_from_order(order)
            result.append(orderstatus)

        return result
Exemplo n.º 27
0
class BacktestTradeApi(BaseTradeApi):
    def __init__(self):
        super(BacktestTradeApi, self).__init__()
        
        self.ctx = None
        
        self._orderbook = OrderBook()
        self.seq_gen = SequenceGenerator()
        self.entrust_no_task_id_map = dict()
        
        self.commission_rate = 0.0
        
    def _get_next_num(self, key):
        """used to generate id for orders and trades."""
        return str(np.int64(self.ctx.trade_date) * 10000 + self.seq_gen.get_next(key))

    def _get_next_task_id(self):
        return np.int64(self.ctx.trade_date) * 10000 + self.seq_gen.get_next('task_id')
    
    def init_from_config(self, props):
        self.commission_rate = props.get('commission_rate', 0.0)
        
        self.set_order_status_callback(lambda ind: self.ctx.strategy.on_order_status(ind))
        self.set_trade_callback(lambda ind: self.ctx.strategy.on_trade(ind))
        self.set_task_status_callback(lambda ind: self.ctx.strategy.on_task_status(ind))

    def on_new_day(self, trade_date):
        self._orderbook = OrderBook()
    
    def use_strategy(self, strategy_id):
        pass
    
    def place_order(self, security, action, price, size, algo="", algo_param={}, userdata=""):
        if size <= 0:
            print("Invalid size {}".format(size))
            return
        
        # Generate Order
        if algo == 'vwap':
            order_type = common.ORDER_TYPE.VWAP
        else:
            order_type = common.ORDER_TYPE.LIMIT
        order = Order.new_order(security, action, price, size, self.ctx.trade_date, self.ctx.time,
                                order_type=order_type)

        # Generate Task
        task_id = self._get_next_task_id()
        order.task_id = task_id
        
        task = Task(task_id,
                    algo=algo, algo_param=algo_param, data=order,
                    function_name='place_order', trade_date=self.ctx.trade_date)
        # task.task_no = task_id
        
        # Send Order to Exchange
        entrust_no = self._orderbook.add_order(order)
        task.data.entrust_no = entrust_no
        
        self.ctx.pm.add_task(task)
        self.entrust_no_task_id_map[entrust_no] = task.task_id

        order_status_ind = OrderStatusInd(order)
        order_status_ind.order_status = common.ORDER_STATUS.ACCEPTED
        # order_status_ind.task_no = task_id
        self._order_status_callback(order_status_ind)


        '''
        # TODO: not necessary
        rsp = OrderRsp(entrust_no=entrust_no, task_id=task_id, msg="")
        self.ctx.instance.strategy.on_order_rsp(rsp)
        '''
        
        return task_id, ""

    def cancel_order(self, task_id):
        task = self.ctx.pm.get_task(task_id)
        if task.function_name == 'place_order':
            order = task.data
            entrust_no = order.entrust_no
            order_status_ind = self._orderbook.cancel_order(entrust_no)
            task_id = self.entrust_no_task_id_map[entrust_no]
            order_status_ind.task_id = task_id
            # order_status_ind.task_no = task_id
            self._order_status_callback(order_status_ind)
        else:
            raise NotImplementedError("cancel task with function_name = {}".format(task.function_name))
    
    def _process_quote(self, df_quote, freq):
        return self._orderbook.make_trade(df_quote, freq)

    def _add_task_id(self, ind):
        no = ind.entrust_no
        task_id = self.entrust_no_task_id_map[no]
        ind.task_id = task_id
        # ind.task_no = task_id
    
    def _add_commission(self, ind):
        comm = calc_commission(ind, self.commission_rate)
        ind.commission = comm
        
    def match_and_callback(self, quote, freq):
        results = self._process_quote(quote, freq)
        
        for trade_ind, order_status_ind in results:
            self._add_commission(trade_ind)
            
            # self._add_task_id(trade_ind)
            # self._add_task_id(order_status_ind)
            task_id = self.entrust_no_task_id_map[trade_ind.entrust_no]

            self._order_status_callback(order_status_ind)
            self._trade_callback(trade_ind)

            task = self.ctx.pm.get_task(task_id)
            if task.is_finished:
                task_ind = TaskInd(task_id, task_status=task.task_status,
                                   task_algo='', task_msg="")
                self._task_status_callback(task_ind)
        
        return results