Esempio n. 1
0
 def df_trades(self, contract_group=None, start_date=None, end_date=None):
     '''
     Returns a dataframe of trades
     
     Args:
         contract_group (:obj:`ContractGroup`, optional): Return trades for this contract group.  
             If None (default), include all contract groups
         start_date (:obj:`np.datetime64`, optional): Include trades with date greater than or equal to this timestamp.
         end_date (:obj:`np.datetime64`, optional): Include trades with date less than or equal to this timestamp.
     '''
     start_date, end_date = str2date(start_date), str2date(end_date)
     trades = self.trades(contract_group, start_date, end_date)
     df = pd.DataFrame.from_records(
         [(trade.contract.symbol, trade.timestamp, trade.qty, trade.price,
           trade.fee, trade.commission, trade.order.timestamp,
           trade.order.qty, trade.order.reason_code,
           (str(trade.order.properties.__dict__)
            if trade.order.properties.__dict__ else ''),
           (str(trade.contract.properties.__dict__)
            if trade.contract.properties.__dict__ else ''))
          for trade in trades],
         columns=[
             'symbol', 'timestamp', 'qty', 'price', 'fee', 'commission',
             'order_date', 'order_qty', 'reason_code', 'order_props',
             'contract_props'
         ])
     df = df.sort_values(by=['timestamp', 'symbol'])
     return df
Esempio n. 2
0
    def run_rules(self,
                  strategy_names: Sequence[str] = None,
                  start_date: np.datetime64 = None,
                  end_date: np.datetime64 = None) -> None:
        '''Run rules for the strategies specified.  Must be called after run_indicators and run_signals.  
          See run function for argument descriptions
        '''
        start_date, end_date = str2date(start_date), str2date(end_date)
        if strategy_names is None:
            strategy_names = list(self.strategies.keys())
        if len(strategy_names) == 0:
            raise Exception('a portfolio must have at least one strategy')

        strategies = [self.strategies[key] for key in strategy_names]

        min_date = min([strategy.timestamps[0] for strategy in strategies])
        if start_date: min_date = max(min_date, start_date)
        max_date = max([strategy.timestamps[-1] for strategy in strategies])
        if end_date: max_date = min(max_date, end_date)

        all_timestamps, iterations = self._generate_order_iterations(
            strategies, start_date, end_date)

        for i, timestamp in enumerate(all_timestamps):
            for (strategy, indices) in iterations:
                # index into strategy timestamps
                idx = indices[i]
                if idx != len(strategy.timestamps
                              ) and strategy.timestamps[idx] == timestamp:
                    strategy._run_iteration(idx)

        # Make sure we calc to the end for each strategy
        for strategy in strategies:
            strategy.account.calc(strategy.timestamps[-1])
Esempio n. 3
0
 def trades(self, contract_group = None, start_date = None, end_date = None):
     '''Returns a list of trades with the given symbol and with trade date between (and including) start date 
         and end date if they are specified. If symbol is None trades for all symbols are returned'''
     start_date, end_date = str2date(start_date), str2date(end_date)
     return [trade for trade in self._trades if (start_date is None or trade.timestamp >= start_date) and (
         end_date is None or trade.timestamp <= end_date) and (
         contract_group is None or trade.contract.contract_group == contract_group)]
Esempio n. 4
0
 def df_trades(self, 
               contract_group: ContractGroup = None, 
               start_date: np.datetime64 = None, 
               end_date: np.datetime64 = None) -> pd.DataFrame:
     '''Returns a dataframe with data from trades with the given contract group and with trade date between (and including)
         start date and end date
         if they are specified. If contract_group is None trades for all contract_groups are returned'''
     start_date, end_date = str2date(start_date), str2date(end_date)
     return self.account.df_trades(contract_group, start_date, end_date)
Esempio n. 5
0
 def trades(self, 
            contract_group: ContractGroup = None, 
            start_date: np.datetime64 = None, 
            end_date: np.datetime64 = None) -> Sequence[Trade]:
     '''Returns a list of trades with the given contract group and with trade date between (and including) start date 
         and end date if they are specified.
         If contract_group is None trades for all contract_groups are returned'''
     start_date, end_date = str2date(start_date), str2date(end_date)
     return self.account.trades(contract_group, start_date, end_date)
Esempio n. 6
0
 def trades(self, symbol=None, start_date=None, end_date=None):
     '''Returns a list of trades with the given symbol and with trade date between (and including) start date and end date if they are specified.
       If symbol is None trades for all symbols are returned'''
     start_date, end_date = str2date(start_date), str2date(end_date)
     if symbol is None:
         trades = []
         for symbol, sym_pnl in self.contract_pnls.items():
             trades += sym_pnl.trades(start_date, end_date)
         return trades
     else:
         return self.contract_pnls[symbol].trades(start_date, end_date)
Esempio n. 7
0
 def df_orders(self, contract_group=None, start_date=None, end_date=None) -> pd.DataFrame:
     '''Returns a dataframe with data from orders with the given contract group and with order date between (and including) 
         start date and end date
         if they are specified. If contract_group is None orders for all contract_groups are returned'''
     start_date, end_date = str2date(start_date), str2date(end_date)
     orders = self.orders(contract_group, start_date, end_date)
     order_records = [(order.contract.symbol, type(order).__name__, order.timestamp, order.qty, 
                       order.reason_code, 
                       (str(order.properties.__dict__) if order.properties.__dict__ else ''),
                       (str(order.contract.properties.__dict__) if order.contract.properties.__dict__ else '')) for order in orders]
     df_orders = pd.DataFrame.from_records(order_records,
                                           columns=['symbol', 'type', 'timestamp', 'qty', 'reason_code', 'order_props', 'contract_props'])
     return df_orders
Esempio n. 8
0
    def df_data(self, 
                contract_groups: Sequence[ContractGroup] = None, 
                add_pnl: bool = True, 
                start_date: Union[str, np.datetime64] = None, 
                end_date: Union[str, np.datetime64] = None) -> pd.DataFrame:
        '''
        Add indicators and signals to end of market data and return as a pandas dataframe.
        
        Args:
            contract_groups (list of:obj:`ContractGroup`, optional): list of contract groups to include.  All if set to None (default)
            add_pnl: If True (default), include P&L columns in dataframe
            start_date: string or numpy datetime64. Default None
            end_date: string or numpy datetime64: Default None
        '''
        start_date, end_date = str2date(start_date), str2date(end_date)
        if contract_groups is None: contract_groups = self.contract_groups
            
        timestamps = self.timestamps
        
        if start_date: timestamps = timestamps[timestamps >= start_date]
        if end_date: timestamps = timestamps[timestamps <= end_date]
            
        dfs = []
             
        for contract_group in contract_groups:
            df = pd.DataFrame({'timestamp': self.timestamps})
            if add_pnl: 
                df_pnl = self.df_pnl(contract_group)
 
            indicator_values = self.indicator_values[contract_group]
            
            for k in sorted(indicator_values.__dict__):
                name = k
                # Avoid name collisions
                if name in df.columns: name = name + '.ind'
                df.insert(len(df.columns), name, getattr(indicator_values, k))

            signal_values = self.signal_values[contract_group]

            for k in sorted(signal_values.__dict__):
                name = k
                if name in df.columns: name = name + '.sig'
                df.insert(len(df.columns), name, getattr(signal_values, k))
                
            if add_pnl: df = pd.merge(df, df_pnl, on=['timestamp'], how='left')
            # Add counter column for debugging
            df.insert(len(df.columns), 'i', np.arange(len(df)))
            
            dfs.append(df)
            
        return pd.concat(dfs)
Esempio n. 9
0
 def trades(self, start_date=None, end_date=None):
     '''Get a list of trades
     
     Args:
         start_date: A string or numpy datetime64.  Trades with trade dates >= start_date will be returned.  Default None
         end_date: A string or numpy datetime64.  Trades with trade dates <= end_date will be returned.  Default None
     '''
     start_date, end_date = str2date(start_date), str2date(end_date)
     trades = [
         trade for trade in self._trades
         if (start_date is None or trade.date >= start_date) and (
             end_date is None or trade.date <= end_date)
     ]
     return trades
Esempio n. 10
0
 def orders(self, 
            contract_group: ContractGroup = None, 
            start_date: Union[np.datetime64, str] = None, 
            end_date: Union[np.datetime64, str] = None) -> Sequence[Order]:
     '''Returns a list of orders with the given contract group and with order date between (and including) start date and 
         end date if they are specified.
         If contract_group is None orders for all contract_groups are returned'''
     orders: List[Order] = []
     start_date, end_date = str2date(start_date), str2date(end_date)
     if contract_group is None:
         orders += [order for order in self._orders if (
             start_date is None or order.timestamp >= start_date) and (end_date is None or order.timestamp <= end_date)]
     else:
         for contract in contract_group.contracts:
             orders += [order for order in self._orders if (contract is None or order.contract == contract) and (
                 start_date is None or order.timestamp >= start_date) and (end_date is None or order.timestamp <= end_date)]
     return orders
Esempio n. 11
0
 def run(self,
         strategy_names: Sequence[str] = None,
         start_date: np.datetime64 = None,
         end_date: np.datetime64 = None) -> None:
     '''
     Run indicators, signals and rules.
     
     Args:
         strategy_names: A list of strategy names.  By default this is set to None and we use all strategies.
         start_date: Run rules starting from this date.  
           Sometimes we have a few strategies in a portfolio that need different lead times before they are ready to trade
           so you can set this so they are all ready by this date.  Default None
         end_date: Don't run rules after this date.  Default None
      '''
     start_date, end_date = str2date(start_date), str2date(end_date)
     self.run_indicators()
     self.run_signals()
     self.run_rules(strategy_names, start_date, end_date)
Esempio n. 12
0
 def df_trades(self, symbol=None, start_date=None, end_date=None):
     '''Returns a dataframe with data from trades with the given symbol and with trade date between (and including) start date and end date
       if they are specified.  If symbol is None, trades for all symbols are returned'''
     start_date, end_date = str2date(start_date), str2date(end_date)
     if symbol:
         trades = self.contract_pnls[symbol].trades(start_date, end_date)
     else:
         trades = [
             v.trades(start_date, end_date)
             for v in self.contract_pnls.values()
         ]
         trades = [trade for sublist in trades
                   for trade in sublist]  # flatten list
     df = pd.DataFrame.from_records(
         [(trade.symbol, trade.date, trade.qty, trade.price, trade.fee,
           trade.commission, trade.order.date, trade.order.qty,
           trade.order.params()) for trade in trades],
         columns=[
             'symbol', 'date', 'qty', 'price', 'fee', 'commission',
             'order_date', 'order_qty', 'order_params'
         ])
     return df
Esempio n. 13
0
 def run_rules(self, 
               rule_names: Sequence[str] = None, 
               contract_groups: Sequence[ContractGroup] = None, 
               start_date: np.datetime64 = None,
               end_date: np.datetime64 = None) -> None:
     '''
     Run trading rules.
     
     Args:
         rule_names: List of rule names.  If None (default) run all rules
         contract_groups: Contract groups to run this rule for.  If None (default), we run it for all contract groups.
         start_date: Run rules starting from this date. Default None 
         end_date: Don't run rules after this date.  Default None
     '''
     start_date, end_date = str2date(start_date), str2date(end_date)
     self._generate_order_iterations(rule_names, contract_groups, start_date, end_date)
     
     # Now we know which rules, contract groups need to be applied for each iteration, go through each iteration and apply them
     # in the same order they were added to the strategy
     for i in range(len(self.orders_iter)):
         self._run_iteration(i)
         
     if self.run_final_calc:
         self.account.calc(self.timestamps[-1])
Esempio n. 14
0
    def _generate_order_iterations(self, 
                                   rule_names: Sequence[str] = None, 
                                   contract_groups: Sequence[ContractGroup] = None, 
                                   start_date: np.datetime64 = None, 
                                   end_date: np.datetime64 = None) -> None:
        '''
        >>> class MockStrat:
        ...    def __init__(self):
        ...        self.timestamps = timestamps
        ...        self.account = self
        ...        self.rules = {'rule_a': rule_a, 'rule_b': rule_b}
        ...        self.market_sims = {ibm: market_sim_ibm, aapl: market_sim_aapl}
        ...        self.rule_signals = {'rule_a': ('sig_a', [1]), 'rule_b': ('sig_b', [1, -1])}
        ...        self.signal_values = {ibm: types.SimpleNamespace(sig_a=np.array([0., 1., 1.]), 
        ...                                                   sig_b = np.array([0., 0., 0.]) ),
        ...                               aapl: types.SimpleNamespace(sig_a=np.array([0., 0., 0.]), 
        ...                                                    sig_b=np.array([0., -1., -1])
        ...                                                   )}
        ...        self.signal_cgroups = {'sig_a': [ibm, aapl], 'sig_b': [ibm, aapl]}
        ...        self.indicator_values = {ibm: types.SimpleNamespace(), aapl: types.SimpleNamespace()}
        >>>
        >>> def market_sim_aapl(): pass
        >>> def market_sim_ibm(): pass
        >>> def rule_a(): pass
        >>> def rule_b(): pass
        >>> timestamps = np.array(['2018-01-01', '2018-01-02', '2018-01-03'], dtype = 'M8[D]')
        >>> rule_names = ['rule_a', 'rule_b']
        >>> ContractGroup.clear()
        >>> ibm = ContractGroup.create('IBM')
        >>> aapl = ContractGroup.create('AAPL')
        >>> contract_groups = [ibm, aapl]
        >>> start_date = np.datetime64('2018-01-01')
        >>> end_date = np.datetime64('2018-02-05')
        >>> strategy = MockStrat()
        >>> Strategy._generate_order_iterations(strategy, rule_names, contract_groups, start_date, end_date)
        >>> orders_iter = strategy.orders_iter
        >>> assert(len(orders_iter[0]) == 0)
        >>> assert(len(orders_iter[1]) == 2)
        >>> assert(orders_iter[1][0][1] == ibm)
        >>> assert(orders_iter[1][1][1] == aapl)
        >>> assert(len(orders_iter[2]) == 0)
        '''
        start_date, end_date = str2date(start_date), str2date(end_date)
        if rule_names is None: rule_names = self.rule_names
        if contract_groups is None: contract_groups = self.contract_groups

        num_timestamps = len(self.timestamps)
        
        # List of lists, i -> list of order tuple
        orders_iter: List[List[OrderTupType]] = [[] for x in range(num_timestamps)]

        for rule_name in rule_names:
            rule_function = self.rules[rule_name]
            for cgroup in contract_groups:
                signal_name, sig_true_values = self.rule_signals[rule_name]
                if cgroup not in self.signal_cgroups[signal_name]:
                    # We don't need to call this rule for this contract group
                    continue
                sig_values = getattr(self.signal_values[cgroup], signal_name)
                timestamps = self.timestamps

                null_value = False if sig_values.dtype == np.dtype('bool') else np.nan
                
                if start_date is not None:
                    start_idx: int = np.searchsorted(timestamps, start_date)  # type: ignore
                    sig_values[0:start_idx] = null_value
                    
                if end_date is not None:
                    end_idx: int = np.searchsorted(timestamps, end_date)  # type: ignore
                    sig_values[end_idx:] = null_value

                indices = np.nonzero(np.isin(sig_values[:num_timestamps], sig_true_values))[0]
                
                # Don't run rules on last index since we cannot fill any orders
                if len(indices) and indices[-1] == len(sig_values) - 1: indices = indices[:-1] 
                indicator_values = self.indicator_values[cgroup]
                iteration_params = {'indicator_values': indicator_values, 'signal_values': sig_values, 'rule_name': rule_name}
                for idx in indices: orders_iter[idx].append((rule_function, cgroup, iteration_params))

        self.orders_iter = orders_iter