def test_merge_pre(ix, pair, timeframe, id, start, end, date_pre, date_post, clean_tmp): ''' Test function 'merge_pre' to merge the 'pre' Segment ''' oanda = OandaAPI(instrument=pair, granularity=timeframe, settingf='../../data/settings.ini') oanda.run(start=start, end=end) candle_list = oanda.fetch_candleset() cl = CandleList(candle_list, instrument=pair, id=id, settingf='../../data/settings.ini') pl = cl.get_pivotlist( th_bounces=cl.settings.getfloat('pivots', 'th_bounces')) pivot = pl.plist[ix] # Check pivot.pre.start() before running 'merge_pre' assert date_post == pivot.pre.start() # run 'merge_pre' function pivot.merge_pre(slist=pl.slist, n_candles=cl.settings.getint('pivots', 'n_candles'), diff_th=cl.settings.getint('pivots', 'diff_th')) # Check pivot.pre.start() after running 'merge_pre' assert date_pre == pivot.pre.start()
def __initclist(self): ''' Private function to initialize the CandleList object that goes from self.trade.start to self.period This will set the self.clist_period class attribute ''' 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 oanda = OandaAPI(url=self.settings.get('oanda_api', 'url'), instrument=self.trade.pair, granularity=self.trade.timeframe, settingf=self.settingf, settings=self.settings) oanda.run(start=start.isoformat(), end=end.isoformat()) 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) self.clist_period = cl
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) oanda.run(start=astart.isoformat(), end=anend.isoformat()) candle_list = oanda.fetch_candleset() cl = CandleList(candle_list, type=self.type, settingf=self.settingf, settings=self.settings) return cl
def __initclist(self): ''' Private function to initialize the CandleList object that goes from self.start to self.period This will set the self.clist_period class attribute ''' delta_period=periodToDelta(self.period, self.timeframe) delta_1=periodToDelta(1, self.timeframe) start = self.start - delta_period # get the start datetime for this CandleList period end = self.start + delta_1 # increase self.start by one candle to include self.start oanda = OandaAPI(url=config.OANDA_API['url'], instrument=self.pair, granularity=self.timeframe, alignmentTimezone=config.OANDA_API['alignmentTimezone'], dailyAlignment=config.OANDA_API['dailyAlignment']) oanda.run(start=start.isoformat(), end=end.isoformat(), roll=True ) candle_list = oanda.fetch_candleset(vol_cutoff=0) cl = CandleList(candle_list, self.pair, granularity=self.timeframe, id=self.id, type=self.type) self.clist_period=cl
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
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 test_indecision_c(pair, timeframe, time, is_it): oanda = OandaAPI(instrument=pair, granularity=timeframe, settingf="../../data/settings.ini") oanda.run(start=time, count=1) candle_list = oanda.fetch_candleset() candle_list[0].set_candle_features() result = candle_list[0].indecision_c() assert is_it == result
def test_OandaAPI_vol(): ''' Test a simple query to Oanda's REST API using a H12 timeframe and using a volume cutoff for 'fetch_candleset' function ''' oanda = OandaAPI(instrument='NZD_JPY', granularity='H12', settingf='../../data/settings.ini') oanda.run(start='2011-09-02T21:00:00', end='2011-09-06T22:00:00') candle_list = oanda.fetch_candleset(vol_cutoff=20) assert len(candle_list) == 6
def test_fetch_one_candle(): oanda = OandaAPI(instrument='AUD_USD', granularity='D', settingf='../../data/settings.ini') oanda.run(start='2015-01-25T22:00:00', count=1) candle_list = oanda.fetch_candleset() assert candle_list[0].highBid == 0.79329 assert candle_list[0].openBid == 0.7873 assert candle_list[0].lowBid == 0.7857 assert candle_list[0].representation == 'bidask' assert candle_list[0].lowAsk == 0.786 assert candle_list[0].complete == True assert candle_list[0].openAsk == 0.7889
def cl_object(): '''Returns a CandleList object''' oanda = OandaAPI(instrument='AUD_USD', granularity='D', settingf='../../data/settings.ini') oanda.run(start='2019-03-06T23:00:00', end='2020-01-03T23:00:00') candle_list = oanda.fetch_candleset() cl = CandleList(candle_list, instrument='AUD_USD', granularity='D', settingf='../../data/settings.ini') return cl
def clO(): """Returns a CandleList object""" oanda = OandaAPI(instrument='AUD_USD', granularity='D', settingf='../../data/settings.ini') oanda.run(start='2019-03-06T23:00:00', end='2020-01-24T23:00:00') candle_list = oanda.fetch_candleset() cl = CandleList(candle_list, instrument='AUD_USD', id='test_AUD_USD_clist', granularity='D', settingf='../../data/settings.ini') return cl
def cl_object(clean_tmp): '''Returns CandleList object''' oanda = OandaAPI(instrument='AUD_USD', granularity='D', settingf='../../data/settings.ini') oanda.run(start='2015-06-24T22:00:00', end='2019-06-21T22:00:00') candle_list = oanda.fetch_candleset() cl = CandleList(candle_list, instrument='AUD_USD', id='AUD_USD_testclist', type='long', settingf='../../data/settings.ini') return cl
def test_set_candle_features(pair, timeframe, time, colour, upper_wick, lower_wick): ''' Test function to set basic candle features based on price i.e. self.colour, upper_wick, etc... ''' oanda = OandaAPI(instrument=pair, granularity=timeframe, settingf="../../data/settings.ini") oanda.run(start=time, count=1) candle_list = oanda.fetch_candleset() candle_list[0].set_candle_features() assert candle_list[0].colour == colour assert candle_list[0].upper_wick == upper_wick assert candle_list[0].lower_wick == lower_wick
def test_calc_itrend(pair, start, end, t_type, itrend, settings_obj, clean_tmp): settings_obj.set('it_trend', 'th_bounces', '0.02') settings_obj.set('it_trend', 'n_candles', '12') oanda = OandaAPI(instrument=pair, granularity='D', settings=settings_obj) oanda.run(start=start.isoformat(), end=end.isoformat()) candle_list = oanda.fetch_candleset() cl = CandleList(candle_list, instrument=pair, id='test_clist', granularity='D', settings=settings_obj) assert itrend == cl.calc_itrend(t_type=t_type)
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 pl_object(): '''Returns PivotList object''' oanda = OandaAPI(instrument='AUD_USD', granularity='D', settingf='../../data/settings.ini') oanda.run(start='2019-03-08T22:00:00', end='2019-08-09T22:00:00') candle_list = oanda.fetch_candleset() cl = CandleList(candle_list, instrument='AUD_USD', id='AUD_USD_test', type='long', settingf='../../data/settings.ini') pl = cl.get_pivotlist( th_bounces=cl.settings.getfloat('pivots', 'th_bounces')) return pl
def set_valley(self): ''' Function to calculate the length of the valley between bounce_1st & bounce_2nd Returns ------- It will set the 'valley' attribute of the class ''' oanda = OandaAPI(url=config.OANDA_API['url'], instrument=self.pair, granularity=self.timeframe, alignmentTimezone=config.OANDA_API['alignmentTimezone'], dailyAlignment=config.OANDA_API['dailyAlignment']) oanda.run(start=self.bounce_2nd.time.isoformat(), end=self.bounce_1st.time.isoformat()) candle_list = oanda.fetch_candleset(vol_cutoff=0) self.valley = len(candle_list)
def test_adjust_pivottime(ix, pair, timeframe, id, start, end, new_b, clean_tmp): oanda = OandaAPI(instrument=pair, granularity=timeframe, settingf='../../data/settings.ini') oanda.run(start=start, end=end) candle_list = oanda.fetch_candleset() cl = CandleList(candle_list, instrument=pair, id=id, settingf='../../data/settings.ini') pl = cl.get_pivotlist( th_bounces=cl.settings.getfloat('pivots', 'th_bounces')) p = pl.plist[ix] newt = p.adjust_pivottime(clistO=cl) assert new_b == newt
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 test_get_lasttime(): oanda = OandaAPI(instrument='AUD_CHF', granularity='H12', settingf='../../data/settings.ini') resist = HArea(price=1.00721, pips=45, instrument='AUD_CHF', granularity='H12', settingf='../../data/settings.ini') oanda.run(start='2004-11-07T10:00:00', end='2010-04-30T09:00:00') candle_list = oanda.fetch_candleset() cl = CandleList(candle_list, instrument='AUD_CHF', id='test_AUD_CHF_clist', granularity='H12', type='short', settingf='../../data/settings.ini') lasttime = cl.get_lasttime(resist) assert lasttime == datetime.datetime(2007, 11, 9, 10, 0)
instrument=args.instrument, granularity=args.granularity, alignmentTimezone='Europe/London', dailyAlignment=22, start=args.start, end=args.end ) else: oanda=OandaAPI(url='https://api-fxtrade.oanda.com/v1/candles?', instrument=args.instrument, granularity=args.granularity, alignmentTimezone='Europe/London', dailyAlignment=22, start=args.start) candle_list=oanda.fetch_candleset() trade_type=str(args.trade_type) cl=CandleList(candle_list, type=trade_type) cl.calc_binary_seq(merge=True) cl.calc_number_of_0s(norm=True) cl.calc_number_of_doubles0s(norm=True) cl.calc_longest_stretch() for k in cl.seq: print("\t{0}: {1}".format(k,cl.seq[k])) print("Number of 0s (merged): %.2f" % cl.number_of_0s['merge']) print("Double0s (highlow): {0}".format(cl.highlow_double0s)) print("Double0s (openclose): {0}".format(cl.openclose_double0s)) print("Stretch:")
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
def __get_time4candles(self, n, anchor_point,roll=True): ''' This private function takes a a number of candles and returns a Datetime corresponding to this number of candles Parameters ---------- n : int Number of candles anchor_point : datetime Datetime used as the anchor (end point from which it will go back 'n' candles) for calculation roll : boolean if True, then if will try to go back in time in order to get exactly 'n' number of candles. Default: True Returns ------- Datetime.datetime ''' delta_from_start = None delta_one = None if self.timeframe == "D": delta_from_start = datetime.timedelta(hours=24 * n) delta_one = datetime.timedelta(hours=24) else: fgran = self.timeframe.replace('H', '') delta_from_start = datetime.timedelta(hours=int(fgran) * n) delta_one = datetime.timedelta(hours=int(fgran)) # calculate the cutoff for the first threshold using the number of candles oanda = OandaAPI(url=config.OANDA_API['url'], instrument=self.pair, granularity=self.timeframe, alignmentTimezone=config.OANDA_API['alignmentTimezone'], dailyAlignment=config.OANDA_API['dailyAlignment']) start = anchor_point - delta_from_start if roll is True: if start< config.START_HIST[self.pair]: #return the first candle in the record if start goes back before the start of the record return config.START_HIST[self.pair] else : end = anchor_point.isoformat() oanda.run(start=start.isoformat(), end=end, roll=True) candle_list = oanda.fetch_candleset() # if len of candle_list is below n then go back one candle at a time while len(candle_list) < n: start = start - delta_one oanda.run(start=start.isoformat(), end=end, roll=True) candle_list = oanda.fetch_candleset() return start else: return start
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")
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 oanda.run(start=start.isoformat(), end=end.isoformat()) 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
def run(self): ''' This function will run the Bot from start to end one candle at a time Returns ------- TradeList object with Trades taken. None if no trades were taken ''' tb_logger.info("Runnning...") 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') 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 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 SRlst = self.calc_SR(adateObj=startO) res = SRlst.print() 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 SRlst = self.calc_SR(adateObj=startO) res = SRlst.print() tb_logger.info("Identified HAreaList for time {0}:".format( startO.isoformat())) tb_logger.info("{0}".format(res)) loop = 0 # fetch candle for current datetime oanda.run(start=startO.isoformat(), count=1) candle_list = oanda.fetch_candleset() c_candle = candle_list[0] # this is the current candle that # is being checked #check if there is any HArea overlapping with c_candle HAreaSel = 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 if prepare_trade is True: t = self.prepare_trade(type=type, SL=SL, ic=c_candle, harea_sel=HAreaSel, delta=delta) t.strat = 'counter' # 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 t.run_trade(expires=2) if t.entered is True: tlist.append(t) tend = t.end startO = startO + delta loop += 1 tb_logger.info("Run done") if len(tlist) == 0: return None else: tl = TradeList(tlist=tlist, settingf=self.settingf, settings=self.settings) tl.analyze() # analyse trades return tl
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' in this case settings=self.settings) elif self.settingf is not None and self.settings is None: oanda = OandaAPI( instrument=self.instrument, granularity=granularity, # 'M30' in this case settingf=self.settingf) elif self.settingf is not None and self.settings is not None: oanda = OandaAPI( instrument=self.instrument, granularity=granularity, # 'M30' in this case settingf=self.settingf) oanda.run(start=cstart.isoformat(), end=cend.isoformat()) 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.'