For code/output blocks: Use ``` (aka backtick or grave accent) in a single line before and after the block. See: http://commonmark.org/help/

Stock Screening


  • administrators

    Use the link below to go the original post

    Click here to see the full blog post



  • How do you stop the output printing:
    """- Datas:
    +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
    - Data0:"""
    for each data source added?


  • administrators

    You cannot with the standard WriterFile which is the one creating the output.

    You can if you subclass it, check the sources, and create your own output.



  • Thank you. How do I subclass it, check the sources and create a different output?


  • administrators

    There is no other way.

    In any case the expected (even if small) API of the Writer should have a small documentation page to ease up subclassing.



  • Thank you for this example. can you please complete it by using this screener as input to a strategy. for instance to be long under and short the long list and close the trades if they go out of the list. thx a lot


  • administrators

    See this reddit post: Reddit - The Conservative Formula in Python: Quantitative Investing made Easy

    The code

    class St(bt.Strategy):
        params = dict(
            rperiod=1,  # period for the returns calculation, default 1 period
            vperiod=36,  # lookback period for volatility - default 36 periods
            mperiod=12,  # lookback period for momentum - default 12 periods
            torank=100,  # number of stocks to rank as top
            reserve=0.05  # 5% reserve capital
        )
    
        def __init__(self):
            # Return, volatility and momentum
            rets = [bt.ind.PctChange(d, period=self.p.rperiod) for d in self.datas]
            vols = [bt.ind.StdDev(ret, period=self.p.vperiod) for ret in rets]
            moms = [bt.ind.ROC(d, period=self.p.mperiod) for d in self.datas]
    
            # simple rank formula: (momentum * net payout) / volatility
            # The highest ranked: low vol, large momentum, large payout
            self.ranks = [d.npy * m / v for d, v, m in zip(self.datas, vols, moms)]
    
            # allocation perc pro stock
            # reserve kept to make sure orders are not rejected due to
            # margin. Prices are calculated when known (close), but orders can only
            # be executed next day (opening price). Price can gap upwards
            self.perctarget = (100.0 - self.p.reserve) % self.p.torank
    
        def next(self):
            # get the ranks sorted for each iteration in a list
            ranks = sorted(
                ((self.datas[i], rank[0]) for i, rank in enumerate(self.ranks)),
                key=lambda x: x[1],  # use rank (elem 1 in the tuple) to sort
                reverse=True,  # highest ranked 1st ... please
            )
            # Put top 100 in ordereddict with data as key to test for presence
            r100 = collections.OrderedDict(ranks[:self.p.torank])
    
            # Remove those no longer Top 100, prepare quick lookup dict
            positions = {pos: pos.data for pos in self.getpositions() if pos}
            for pos, data in positions.items():
                if data not in r100:  # open and no rank100 ... close
                    self.order_target_percent(data, target=0.0)
    
            # Add the newcomers to the Top 100, prepare easy reverse lookup
            revpositions = {data: pos for pos, data in positions.items()}
            for data in (d for d in r100 if d not in revpositions):
                self.order_target_percent(data, target=self.perctarget)
    


  • @backtrader You are awesome!! many thanks



  • @backtrader Hi, I've tryed to test your code but unfortunatly it didn't work properly as the compiler do not reconignize "pos.data" from this line "{pos: pos.data for pos in self.getpositions() if pos}" and also i suspect this line too "self.order_target_percent(data, target=self.perctarget)" the self.order_target percent part. Can you please share the full code that works for you?



  • from backtrader.feeds import PandasData

    class PycaishenData(PandasData):

    # Add a 'pe' line to the inherited ones from the base class
    lines = ('turnover','mkt_cap')
    
    # openinterest in GenericCSVData has index 7 ... add 1
    # add the parameter to the parameters inherited from the base class
    params = (('turnover', -1),('mkt_cap',-1))
    

    class StAmine(bt.Strategy):
    params = dict(
    rperiod=1, # period for the returns calculation, default 1 period
    vperiod=36, # lookback period for volatility - default 36 periods
    mperiod=12, # lookback period for momentum - default 12 periods
    torank=10, # number of stocks to rank as top
    reserve=0.05 # 5% reserve capital
    )

    def __init__(self):
    
    
        # The highest ranked: mkt_cap
    
        self.ranks = [bt.ind.movav(d.mkt_cap,period = 1) for d in self.datas]
    
        # allocation perc pro stock
        # reserve kept to make sure orders are not rejected due to
        # margin. Prices are calculated when known (close), but orders can only
        # be executed next day (opening price). Price can gap upwards
        self.perctarget = (100.0 - self.p.reserve) % self.p.torank
    
    def next(self):
        # get the ranks sorted for each iteration in a list
        ranks = sorted(
            ((self.datas[i], rank[0]) for i, rank in enumerate(self.ranks)),
            key=lambda x: x[1],  # use rank (elem 1 in the tuple) to sort
            reverse=True,  # highest ranked 1st ... please
        )
        # Put top 100 in ordereddict with data as key to test for presence
        r100 = collections.OrderedDict(ranks[:self.p.torank])
    
        # Remove those no longer Top 100, prepare quick lookup dict
        positions = {pos: pos.data for pos in self.getpositions().values() if pos}
        for pos, data in positions.items():
            if data not in r100:  # open and no rank100 ... close
                self.order_target_percent(data, target=0.0)
    
        # Add the newcomers to the Top 100, prepare easy reverse lookup
        revpositions = {data: pos for pos, data in positions.items()}
        for data in (d for d in r100 if d not in revpositions):
            self.order_target_percent(data, target=self.perctarget)
    

    def run(tickers, datas,strategy = StAmine,plotdetails= True,writedetail = False):

    cerebro = bt.Cerebro()
    
    print ('Starting Portfolio Value: %.2f ' % cerebro.broker.getvalue())
    
    cerebro.addstrategy(strategy)
    
    for ticker, data in zip(tickers , datas):
        dt = PycaishenData(dataname=data)
        if plotdetails == False:
            dt.plotinfo.plot = False
        cerebro.adddata(dt,ticker)
    
    cerebro.broker.set_fundmode(True)
    cerebro.broker.set_fundstartval(10.0)  # the default is 100
    cerebro.broker.setcommission(commission=0.0025)
    
    # Add the Analyzers
    cerebro.addanalyzer(btanalyser.SQN)
    cerebro.addanalyzer(btanalyser.AnnualReturn)
    cerebro.addanalyzer(btanalyser.SharpeRatio)
    
    cerebro.broker.setcash(100000.0)
    
    cerebro.addanalyzer(btanalyser.TradeAnalyzer)
    
    if writedetail:
        cerebro.addwriter(bt.WriterFile, csv=True, rounding=4)
    
    cerebro.run()
    
    cerebro.plot()
    
    print ('Final portfolio value: %.2f' % cerebro.broker.getvalue())
    

    run(tickers, datas,strategy = StAmine,plotdetails= False,writedetail = False)

    the code seems to do nothing!!


  • administrators

    @spyamine said in Stock Screening:

    Hi, I've tryed to test your code but unfortunatly it didn't work properly as the compiler do not reconignize "pos.data" from this line "{pos: pos.data for pos in self.getpositions() if pos}" and also i suspect this line too "self.order_target_percent(data, target=self.perctarget)" the self.order_target percent part. Can you please share the full code that works for you?

    It is a hand-crafted snippet, giving an indication and orientation as to how a proposed algorithm could be implemented.