Example #1
0
    def __initclist(self, timeframe, end, pair):
        '''
        Private function to initialize the CandleList object that goes from trade.start
        to self.settings.getint('counter', 'period')

        Parameters
        ----------
        timeframe : Timeframe
        end : Datetime object that will be the end of this CandleList
        pair : pair

        Returns
        -------
        Candlelist or
        None if Oanda API query was not successful
        '''

        delta_period = periodToDelta(self.settings.getint('trade_bot', 'period_range'),
                                     timeframe)
        delta_1 = periodToDelta(1, timeframe)
        start = end - delta_period  # get the start datetime for this CandleList period
        end = end + delta_1  # increase end by one candle to include end

        tb_logger.debug("Fetching candlelist for period: {0}-{1}".format(start, end))

        oanda = OandaAPI(url=self.settings.get('oanda_api', 'url'),
                         instrument=pair,
                         granularity=timeframe,
                         settingf=self.settingf,
                         settings=self.settings)

        if self.ser_data_obj is None:
            tb_logger.info("Fetching data from API")
            resp = oanda.run(start=start.isoformat(),
                             end=end.isoformat())
        else:
            tb_logger.info("Fetching data from File")
            oanda.data = self.ser_data_obj.slice(start=start,
                                                 end=end)
            resp = 200

        if resp == 200:
            candle_list = oanda.fetch_candleset()

            cl = CandleList(candle_list,
                            settingf=self.settingf,
                            settings=self.settings,
                            instrument=pair,
                            granularity=timeframe,
                            ser_data_obj=self.ser_data_obj,
                            id="test",
                            type="short")
            cl.calc_rsi()
            return cl
        else:
            t_logger.warn("API query was not OK. No CandleList created ")
            return None
    def __initclist(self):
        '''
        Private function to initialize the CandleList object that goes from self.trade.start
        to self.settings.getint('counter', 'period')

        This will set the self.clist_period class attribute

        Returns
        -------
        None if Oanda API query was not successful
        '''

        delta_period = periodToDelta(self.settings.getint('counter', 'period'),
                                     self.trade.timeframe)
        delta_1 = periodToDelta(1, self.trade.timeframe)
        start = self.trade.start - delta_period  # get the start datetime for this CandleList period
        end = self.trade.start + delta_1  # increase self.start by one candle to include self.start

        c_logger.debug("Fetching candlelist for period: {0}-{1}".format(
            start, end))

        oanda = OandaAPI(url=self.settings.get('oanda_api', 'url'),
                         instrument=self.trade.pair,
                         granularity=self.trade.timeframe,
                         settingf=self.settingf,
                         settings=self.settings)

        resp = None
        if self.ser_data_obj is None:
            c_logger.debug("Fetching data from API")
            resp = oanda.run(start=start.isoformat(), end=end.isoformat())
        else:
            c_logger.debug("Fetching data from File")
            oanda.data = self.ser_data_obj.slice(start=start, end=end)
            resp = 200

        if resp == 200:
            candle_list = oanda.fetch_candleset()

            cl = CandleList(candle_list,
                            settingf=self.settingf,
                            settings=self.settings,
                            instrument=self.trade.pair,
                            granularity=self.trade.timeframe,
                            id=self.trade.id,
                            ser_data_obj=self.ser_data_obj,
                            type=self.trade.type)

            self.clist_period = cl
        else:
            c_logger.warn(
                "API query was not OK. 'self.clist_period' will be None ")
            self.clist_period = None
Example #3
0
    def get_max_min(self, adateObj):
        '''
        Function to get the price range for identifying S/R by checking the max
        and min price for CandleList starting in
        'adateObj'- self.settings.getint('trade_bot', 'period_range') and ending in 'adateObj'

        Parameters
        ----------
        datetime object used for identifying
        S/R areas

        Returns
        -------
        max, min floats
        '''

        oanda = OandaAPI(instrument=self.pair,
                         granularity=self.timeframe,
                         settingf=self.settingf,
                         settings=self.settings)

        delta_period = periodToDelta(self.settings.getint('trade_bot', 'period_range'),
                                     self.timeframe)
        delta_1 = periodToDelta(1, self.timeframe)

        start = adateObj - delta_period  # get the start datetime for this CandleList period
        end = adateObj + delta_1  # increase self.start by one candle to include self.start

        if self.ser_data_obj is None:
            tb_logger.info("Fetching data from API")
            oanda.run(start=start.isoformat(),
                      end=end.isoformat())
        else:
            tb_logger.info("Fetching data from File")
            oanda.data = self.ser_data_obj.slice(start=start,
                                                 end=end)
        candle_list = oanda.fetch_candleset()
        cl = CandleList(candle_list,
                        instrument=self.pair,
                        id='test',
                        granularity=self.timeframe,
                        settingf=self.settingf,
                        settings=self.settings)

        max = cl.get_highest()
        min = cl.get_lowest()

        # add a number of pips to max,min to be sure that we
        # also detect the extreme pivots
        max = add_pips2price(self.pair, max, self.settings.getint('trade_bot', 'add_pips'))
        min = substract_pips2price(self.pair, min, self.settings.getint('trade_bot', 'add_pips'))

        return max, min
    def calc_pips_c_trend(self):
        '''
        Function to calculate the pips_c_trend

        Returns
        -------
        Float with number of pips for the trend_i
        '''

        oanda = OandaAPI(url=self.settings.get('oanda_api', 'url'),
                         instrument=self.trade.pair,
                         granularity=self.trade.timeframe,
                         settingf=self.settingf,
                         settings=self.settings)

        resp = None
        if self.ser_data_obj is None:
            c_logger.debug("Fetching data from API")
            resp = oanda.run(start=self.trend_i.isoformat(),
                             end=self.trade.start.isoformat())
        else:
            c_logger.debug("Fetching data from File")
            oanda.data = self.ser_data_obj.slice(start=self.trend_i,
                                                 end=self.trade.start)
            resp = 200

        if resp == 200:

            candle_list = oanda.fetch_candleset()

            cl = CandleList(candle_list,
                            settingf=self.settingf,
                            settings=self.settings,
                            instrument=self.trade.pair,
                            granularity=self.trade.timeframe,
                            id=self.trade.id,
                            type=self.trade.type)

            pips_c_trend = cl.get_length_pips() / cl.get_length_candles()

            return round(pips_c_trend, 1)
        else:
            c_logger.warn(
                "API query was not OK. 'pips_c_trend' could not be calculated")
    def fetch_candlelist(self):
        '''
        This function returns a CandleList object for this Trade

        Returns
        -------

        A CandleList object

        '''
        oanda = OandaAPI(instrument=self.pair,
                         granularity=self.timeframe,
                         settingf=self.settingf,
                         settings=self.settings)

        if isinstance(self.start, datetime) is True:
            astart = self.start
        else:
            astart = try_parsing_date(self.start)

        if isinstance(self.end, datetime) is True:
            anend = self.end
        else:
            anend = try_parsing_date(self.end)

        if self.ser_data_obj is None:
            t_logger.debug("Fetching data from API")
            oanda.run(start=astart.isoformat(), end=anend.isoformat())
        else:
            t_logger.debug("Fetching data from File")
            oanda.data = self.ser_data_obj.slice(start=astart, end=anend)

        candle_list = oanda.fetch_candleset()
        cl = CandleList(candle_list,
                        type=self.type,
                        settingf=self.settingf,
                        settings=self.settings)

        return cl
    def calc_rsi(self):
        '''
        Calculate the RSI for a certain candle list
        '''
        cl_logger.debug("Running calc_rsi")

        start_time = self.clist[0].time
        end_time = self.clist[-1].time

        delta_period = None
        if self.granularity == "D":
            delta_period = timedelta(hours=24 * self.settings.getint('candlelist', 'period'))
        else:
            fgran = self.granularity.replace('H', '')
            delta_period = timedelta(hours=int(fgran) * self.settings.getint('candlelist', 'period'))

        start_calc_time = start_time - delta_period

        #fetch candle set from start_calc_time
        oanda = OandaAPI(instrument=self.instrument,
                         granularity=self.granularity,
                         settingf=self.settingf,
                         settings=self.settings)
        '''
        Get candlelist from start_calc_time to (start_time-1)
        This 2-step API call is necessary in order to avoid
        maximum number of candles errors
        '''
        if self.ser_data_obj is None:
            cl_logger.debug("Fetching data from API")
            oanda.run(start=start_calc_time.isoformat(),
                      end=start_time.isoformat())
        else:
            cl_logger.debug("Fetching data from File")
            oanda.data = self.ser_data_obj.slice(start=start_calc_time,
                                                 end=start_time)

        cl1 = oanda.fetch_candleset()

        '''Get candlelist from start_time to end_time'''
        if self.ser_data_obj is None:
            cl_logger.debug("Fetching data from API")
            oanda.run(start=start_time.isoformat(),
                      end=end_time.isoformat())
        else:
            cl_logger.debug("Fetching data from File")
            oanda.data = self.ser_data_obj.slice(start=start_time,
                                                 end=end_time)

        cl2 = oanda.fetch_candleset()

        if cl1[-1].time == cl2[0].time:
            del cl1[-1]

        candle_list = cl1 + cl2

        series = [c.closeAsk for c in candle_list]

        df = pd.DataFrame({'close': series})
        chg = df['close'].diff(1)

        gain = chg.mask(chg < 0, 0)
        loss = chg.mask(chg > 0, 0)

        rsi_period = self.settings.getint('candlelist', 'rsi_period')
        avg_gain = gain.ewm(com=rsi_period - 1, min_periods=rsi_period).mean()
        avg_loss = loss.ewm(com=rsi_period - 1, min_periods=rsi_period).mean()

        rs = abs(avg_gain / avg_loss)

        rsi = 100 - (100 / (1 + rs))

        rsi4cl = rsi[-len(self.clist):]
        # set rsi attribute in each candle of the CandleList
        ix = 0
        for c, v in zip(self.clist, rsi4cl):
            self.clist[ix].rsi = round(v, 2)
            ix += 1

        cl_logger.debug("Done calc_rsi")
    def run_trade(self, expires=None):
        '''
        Run the trade until conclusion from a start date
        '''

        t_logger.info("Run run_trade with id: {0}".format(self.id))

        entry = HArea(price=self.entry,
                      instrument=self.pair,
                      pips=self.settings.getint('trade', 'hr_pips'),
                      granularity=self.timeframe,
                      ser_data_obj=None,
                      settings=self.settings)
        SL = HArea(price=self.SL,
                   instrument=self.pair,
                   pips=self.settings.getint('trade', 'hr_pips'),
                   granularity=self.timeframe,
                   ser_data_obj=None,
                   settings=self.settings)
        TP = HArea(price=self.TP,
                   instrument=self.pair,
                   pips=self.settings.getint('trade', 'hr_pips'),
                   granularity=self.timeframe,
                   ser_data_obj=None,
                   settings=self.settings)

        period = None
        if self.timeframe == "D":
            period = 24
        else:
            period = int(self.timeframe.replace('H', ''))

        # generate a range of dates starting at self.start and ending numperiods later in order to assess the outcome
        # of trade and also the entry time

        self.start = datetime.strptime(str(self.start), '%Y-%m-%d %H:%M:%S')
        numperiods = self.settings.getint('trade', 'numperiods')
        # date_list will contain a list with datetimes that will be used for running self
        date_list = [
            datetime.strptime(str(self.start.isoformat()), '%Y-%m-%dT%H:%M:%S')
            + timedelta(hours=x * period) for x in range(0, numperiods)
        ]

        count = 0
        self.entered = False

        for d in date_list:
            count += 1
            if expires is not None:
                if count > expires and self.entered is False:
                    self.outcome = 'n.a.'
                    break
            oanda = OandaAPI(instrument=self.pair,
                             granularity=self.timeframe,
                             settingf=self.settingf,
                             settings=self.settings)

            if self.ser_data_obj is None:
                t_logger.debug("Fetching data from API")
                oanda.run(start=d.isoformat(), count=1)
            else:
                t_logger.debug("Fetching data from File")
                oanda.data = self.ser_data_obj.slice(start=d, count=1)

            cl = oanda.fetch_candleset()[0]
            if self.entered is False:
                entry_time = entry.get_cross_time(
                    candle=cl,
                    granularity=self.settings.get('trade', 'granularity'))
                if entry_time != 'n.a.':
                    t_logger.info("Trade entered")
                    # modify self.start to the datetime
                    # that Trade has actually entered
                    self.start = d
                    self.entry_time = entry_time.isoformat()
                    self.entered = True
            if self.entered is True:
                # will be n.a. is cl does not cross SL
                failure_time = SL.get_cross_time(candle=cl,
                                                 granularity=self.settings.get(
                                                     'trade', 'granularity'))
                # sometimes there is a jump in the price and SL is not crossed
                is_gap = False
                if (self.type == "short" and cl.lowAsk > SL.price) or (
                        self.type == "long" and cl.highAsk < SL.price):
                    is_gap = True
                    failure_time = d
                if (failure_time is not None
                        and failure_time != 'n.a.') or is_gap is True:
                    self.outcome = 'failure'
                    self.end = failure_time
                    self.pips = float(
                        calculate_pips(self.pair,
                                       abs(self.SL - self.entry))) * -1
                    t_logger.info("S/L was hit")
                    break
                # will be n.a. if cl does not cross TP
                success_time = TP.get_cross_time(candle=cl,
                                                 granularity=self.settings.get(
                                                     'trade', 'granularity'))
                # sometimes there is a jump in the price and TP is not crossed
                is_gap = False
                if (self.type == "short" and cl.highAsk < TP.price) or (
                        self.type == "long" and cl.lowAsk > TP.price):
                    is_gap = True
                    success_time = d

                if (success_time is not None
                        and success_time != 'n.a.') or is_gap is True:
                    self.outcome = 'success'
                    t_logger.info("T/P was hit")
                    self.end = success_time
                    self.pips = float(
                        calculate_pips(self.pair, abs(self.TP - self.entry)))
                    break
        try:
            assert getattr(self, 'outcome')
        except:
            t_logger.warning("No outcome could be calculated")
            self.outcome = "n.a."
            self.pips = 0

        t_logger.info("Done run_trade")
Example #8
0
    def get_cross_time(self, candle, granularity='M30'):
        '''
        This function is used get the time that the candle
        crosses (go through) HArea

        Parameters
        ----------
        candle :   Candle object that crosses the HArea
        granularity : To what granularity we should descend

        Returns
        ------
        datetime object with crossing time.
                 n.a. if crossing time could not retrieved. This can happens
                 when there is an artefactual jump in Oanda's data
        '''
        if candle.lowAsk <= self.price <= candle.highAsk:

            delta = None
            if self.granularity == "D":
                delta = timedelta(hours=24)
            else:
                fgran = self.granularity.replace('H', '')
                delta = timedelta(hours=int(fgran))

            cstart = candle.time
            cend = cstart + delta
            oanda = None
            if self.settingf is None and self.settings is None:
                raise Exception(
                    "No 'settings' nor 'settingf' defined for this object")
            elif self.settingf is None and self.settings is not None:
                oanda = OandaAPI(
                    instrument=self.instrument,
                    granularity=granularity,  # 'M30' is the default
                    settings=self.settings)
            elif self.settingf is not None and self.settings is None:
                oanda = OandaAPI(
                    instrument=self.instrument,
                    granularity=granularity,  # 'M30' is the default
                    settingf=self.settingf)
            elif self.settingf is not None and self.settings is not None:
                oanda = OandaAPI(
                    instrument=self.instrument,
                    granularity=granularity,  # 'M30' is the default
                    settingf=self.settingf)

            if self.ser_data_obj is None:
                h_logger.debug("Fetching data from API")
                oanda.run(start=cstart.isoformat(), end=cend.isoformat())
            else:
                h_logger.debug("Fetching data from File")
                oanda.data = self.ser_data_obj.slice(start=cstart, end=cend)

            candle_list = oanda.fetch_candleset()
            seen = False
            for c in candle_list:
                if c.lowAsk <= self.price <= c.highAsk:
                    seen = True
                    return c.time

            if seen is False:
                return 'n.a.'
        else:
            return 'n.a.'
Example #9
0
    def __get_trade_type(self, ic, delta):
        '''
        Function to guess what is the trade type (short or long)
        for this possible trade. It will also adjust the SL price
        to the most recent highest high/lowest low

        Parameters
        ----------
        ic : Candle object
             Indecision candle for this trade
        delta : Timedelta object corresponding to
                the time that needs to be increased

        Returns
        -------
        str: Trade type (long or short)
        float: adjusted SL
        '''

        oanda = OandaAPI(instrument=self.pair,
                         granularity=self.timeframe,
                         settingf=self.settingf,
                         settings=self.settings)

        # n x delta controls how many candles to go back in time
        # to check
        start = ic.time - 20*delta
        end = ic.time

        if self.ser_data_obj is None:
            tb_logger.info("Fetching data from API")
            oanda.run(start=start.isoformat(),
                      end=end.isoformat())
        else:
            tb_logger.info("Fetching data from File")
            oanda.data = self.ser_data_obj.slice(start=start,
                                                 end=end)

        candle_list = oanda.fetch_candleset()
        clObj = CandleList(candle_list,
                           instrument=self.pair,
                           id='fit_reg',
                           granularity=self.timeframe,
                           settingf=self.settingf,
                           settings=self.settings)
        # fit a regression line in order to check its slope
        # and guess the trade type
        (fitted_model, regression_model_mse) = clObj.fit_reg_line()

        slope = fitted_model.coef_[0][0]

        if slope < 0:
            type = 'long'
        elif slope > 0:
            type = 'short'

        # adjust SL
        if type == 'short':
            part = 'high{0}'.format(self.settings.get('general', 'bit'))
        elif type == 'long':
            part = 'low{0}'.format(self.settings.get('general', 'bit'))

        SL = None
        for c in reversed(candle_list):
            price = getattr(c, part)
            if SL is None:
                SL = price
                continue
            if type == 'short':
                if price > SL:
                    SL = price
            if type == 'long':
                if price < SL:
                    SL = price

        return type, SL
Example #10
0
    def run(self, discard_sat=True, pickled_file=None):
        '''
        This function will run the Bot from start to end
        one candle at a time

        Parameter
        ---------
        discard_sat : Bool
                      If this is set to True, then the Trade wil not
                      be taken if IC falls on a Saturday. Default: True
        pickled_file : str
                       Path used to dump the pickled representation of the dict
                       of HAreaList generated by store_SRlist (if self.settings.
                       getboolean('trade_bot', 'store_SRlist' is True
                       Or path used to load the pickled representation (if
                       self.settings.getboolean('trade_bot', 'load_SRlist' is
                       True)

        Returns
        -------
        TradeList object with Trades taken. None if no trades
        were taken
        '''
        tb_logger.info("Running...")

        oanda = OandaAPI(instrument=self.pair,
                         granularity=self.timeframe,
                         settingf=self.settingf,
                         settings=self.settings)
        delta = None
        nhours = None
        if self.timeframe == "D":
            nhours = 24
            delta = datetime.timedelta(hours=24)
        else:
            p1 = re.compile('^H')
            m1 = p1.match(self.timeframe)
            if m1:
                nhours = int(self.timeframe.replace('H', ''))
                delta = datetime.timedelta(hours=int(nhours))

        startO = pd.datetime.strptime(self.start, '%Y-%m-%d %H:%M:%S')
        endO = pd.datetime.strptime(self.end, '%Y-%m-%d %H:%M:%S')

        dict_SRlist = {}
        if self.settings.getboolean('trade_bot', 'load_SRlist') is True:
            tb_logger.info("Loading dict of HAreaLists from pickled "
                           "file: {0}".format(pickled_file))
            pickled_fh = open(pickled_file, 'rb')
            dict_SRlist = pickle.load(pickled_fh)

        SRlst = None
        loop = 0
        tlist = []
        tend = None
        while startO <= endO:
            if tend is not None:
                # this means that there is currently an active trade
                if startO <= tend:
                    startO = startO + delta
                    loop += 1
                    continue
                else:
                    tend = None
            tb_logger.info("Trade bot - analyzing candle: {0}".format(startO.isoformat()))
            if loop == 0:
                # no iteration has occurred yet, so invoke .calc_SR for the first time
                if self.settings.getboolean('trade_bot', 'load_SRlist') is True:
                    SRlst= dict_SRlist[startO]
                else:
                    SRlst = self.calc_SR(adateObj=startO)
                res = SRlst.print()
                dict_SRlist[startO] = SRlst
                tb_logger.info("Identified HAreaList for time {0}:".format(startO.isoformat()))
                tb_logger.info("{0}".format(res))
            elif loop >= self.settings.getint('trade_bot',
                                              'period'):
                # An entire cycle has occurred. Invoke .calc_SR
                if self.settings.getboolean('trade_bot', 'load_SRlist') is True:
                    SRlst = dict_SRlist[startO]
                else:
                    SRlst = self.calc_SR(adateObj=startO)
                res = SRlst.print()
                dict_SRlist[startO] = SRlst
                tb_logger.info("Identified HAreaList for time {0}:".format(startO.isoformat()))
                tb_logger.info("{0}".format(res))
                loop = 0

            # fetch candle for current datetime
            if self.ser_data_obj is None:
                tb_logger.info("Fetching data from API")
                oanda.run(start=startO.isoformat(),
                          count=1)
            else:
                tb_logger.info("Fetching data from File")
                oanda.data = self.ser_data_obj.slice(start=startO,
                                                     count=1)

            candle_list = oanda.fetch_candleset()
            c_candle = candle_list[0] # this is the current candle that
                                      # is being checked

            # c_candle.time is not equal to startO
            # when startO is non-working day, for example
            delta1hr = datetime.timedelta(hours=1)
            if (c_candle.time != startO) and (abs(c_candle.time-startO) > delta1hr):
                loop += 1
                tb_logger.info("Analysed dt {0} is not the same than APIs returned dt {1}."
                               " Skipping...".format(startO, c_candle.time))
                startO = startO + delta
                continue

            #check if there is any HArea overlapping with c_candle
            HAreaSel, sel_ix = SRlst.onArea(candle=candle_list[0])

            if HAreaSel is not None:
                c_candle.set_candle_features()
                # guess the if trade is 'long' or 'short'
                type, SL = self.__get_trade_type(ic=c_candle, delta=delta)
                prepare_trade = False
                if c_candle.indecision_c(ic_perc=self.settings.getint('general', 'ic_perc')) is True:
                    prepare_trade = True
                elif type == 'short' and c_candle.colour == 'red':
                    prepare_trade = True
                elif type == 'long' and c_candle.colour == 'green':
                    prepare_trade = True

                # discard if IC falls on a Saturday
                if c_candle.time.weekday() == 5 and discard_sat is True:
                    tb_logger.info("Possible trade at {0} falls on Sat. Skipping...".format(c_candle.time))
                    prepare_trade = False

                if prepare_trade is True:
                    t = self.prepare_trade(
                        type=type,
                        SL=SL,
                        ic=c_candle,
                        harea_sel=HAreaSel,
                        delta=delta)
                    t.strat = 'counter'
                    t.tot_SR = len(SRlst.halist)
                    t.rank_selSR = sel_ix
                    # calculate t.entry-t.SL in number of pips
                    # and discard if it is over threshold
                    diff = abs(t.entry-t.SL)
                    number_pips = float(calculate_pips(self.pair, diff))
                    if number_pips > self.settings.getint('trade_bot', 'SL_width_pips'):
                        loop += 1
                        startO = startO + delta
                        continue
                    if self.settings.getboolean('trade_bot', 'run_trades') is True:
                        t.run_trade(expires=2)
                        if t.entered is True:
                            if not hasattr(t, 'end'):
                                tb_logger.info("Trade.end will be n.a. Check if this trade hit the SL/TP in"
                                               "the analysed timeframe. Skipping...")
                                tend = None
                            else:
                                tlist.append(t)
                                tend = t.end
                    else:
                        tlist.append(t)
            startO = startO+delta
            loop += 1

        tb_logger.info("Run done")

        if self.settings.getboolean('trade_bot', 'store_SRlist') is True:
            tb_logger.info("Dumping dict of HAreaLists to pickled "
                           "file: {0}".format(pickled_file))
            pickled_lst_f = open(pickled_file, 'wb')
            pickle.dump(dict_SRlist, pickled_lst_f)
            pickled_lst_f.close()

        if len(tlist) == 0:
            return None
        else:
            tl = TradeList(tlist=tlist,
                           settingf=self.settingf,
                           settings=self.settings,
                           ser_data_obj=self.ser_data_obj
                           )

            if self.settings.getboolean('trade_bot', 'run_trades') is True:
                # analyse trades
                tl.analyze()
            return tl