コード例 #1
0
def test_is_market_open():
    timezone = "Europe/London"
    tz = pytz.timezone(timezone)
    now_time = datetime.now(tz=tz).strftime("%H:%M")
    expected = Utils.is_between(
        str(now_time),
        ("07:55", "16:35")) and BankHolidays().is_work_day(datetime.now(tz=tz))

    result = Utils.is_market_open(timezone)

    assert result == expected
コード例 #2
0
 def calculate_stop_limit(
     self, tradeDirection, current_offer, current_bid, limit_perc, stop_perc
 ):
     """
     Calculate the stop and limit levels from the given percentages
     """
     limit = None
     stop = None
     if tradeDirection == TradeDirection.BUY:
         limit = current_offer + Utils.percentage_of(limit_perc, current_offer)
         stop = current_bid - Utils.percentage_of(stop_perc, current_bid)
     elif tradeDirection == TradeDirection.SELL:
         limit = current_bid - Utils.percentage_of(limit_perc, current_bid)
         stop = current_offer + Utils.percentage_of(stop_perc, current_offer)
     return limit, stop
コード例 #3
0
ファイル: TradingBot.py プロジェクト: andrebask/TradingBot
 def wait_for_next_market_opening(self):
     """
     Sleep until the next market opening. Takes into account weekends
     and bank holidays in UK
     """
     seconds = Utils.get_seconds_to_market_opening(dt.datetime.now())
     logging.info("Market is closed! Wait for {0:.2f} hours...".format(
         seconds / 3600))
     time.sleep(seconds)
コード例 #4
0
 def is_market_open(self, timezone):
     """
     Return True if the market is open, false otherwise
         - **timezone**: string representing the timezone
     """
     tz = pytz.timezone(timezone)
     now_time = datetime.now(tz=tz).strftime("%H:%M")
     return BankHolidays().is_work_day(datetime.now(tz=tz)) and Utils.is_between(
         str(now_time), ("07:55", "16:35")
     )
コード例 #5
0
    def get_account_used_perc(self):
        """
        Fetch the percentage of available balance is currently used

            - Returns the percentage of account used over total available amount
        """
        balance, deposit = self.get_account_balances()
        if balance is None or deposit is None:
            return None
        return Utils.percentage(deposit, balance)
コード例 #6
0
def test_get_seconds_to_market_opening():
    now = datetime.now()
    seconds = Utils.get_seconds_to_market_opening(now)
    assert seconds > 0
    assert seconds is not None
    opening = now + timedelta(seconds=seconds)
    assert 0 <= opening.weekday() < 5
    expected = opening.replace(hour=8, minute=0, second=2, microsecond=0)
    diff = expected - opening
    assert diff.seconds < 10
    # Test function if called after midnight but before market opening
    mock = datetime.now().replace(hour=3, minute=30, second=0, microsecond=0)
    seconds = Utils.get_seconds_to_market_opening(mock)
    assert seconds > 0
    assert seconds is not None
    opening = mock + timedelta(seconds=seconds)
    # assert opening.day == mock.day
    assert opening.hour == 8
    assert opening.minute == 0
コード例 #7
0
ファイル: TradingBot.py プロジェクト: andrebask/TradingBot
    def safety_checks(self):
        """
        Perform some safety checks before running the strategy against the next market

        Return True if the trade can proceed, False otherwise
        """
        percent_used = self.broker.get_account_used_perc()
        if percent_used is None:
            logging.warning(
                "Stop trading because can't fetch percentage of account used")
            raise NotSafeToTradeException()
        if percent_used >= self.max_account_usable:
            logging.warning(
                "Stop trading because {}% of account is used".format(
                    str(percent_used)))
            raise NotSafeToTradeException()
        if not Utils.is_market_open(self.time_zone):
            logging.warning("Market is closed: stop processing")
            raise MarketClosedException()
コード例 #8
0
    def get_prices(self, epic_id, interval, data_range):
        """
        Returns past prices for the given epic

            - **epic_id**: market epic as string
            - **interval**: resolution of the time series: minute, hours, etc.
            - **data_range**: amount of datapoint to fetch
            - Returns **None** if an error occurs otherwise the json object returned by IG API
        """
        url = "{}/{}/{}/{}/{}".format(self.apiBaseURL, IG_API_URL.PRICES.value,
                                      epic_id, interval, data_range)
        d = self.http_get(url)
        if d is not None and "allowance" in d:
            remaining_allowance = d["allowance"]["remainingAllowance"]
            reset_time = Utils.humanize_time(
                int(d["allowance"]["allowanceExpiry"]))
            if remaining_allowance < 100:
                logging.warn("Remaining API calls left: {}".format(
                    str(remaining_allowance)))
                logging.warn("Time to API Key reset: {}".format(
                    str(reset_time)))
        return d
コード例 #9
0
    def find_trade_signal(self, market, datapoints):
        """
        TODO add description of strategy key points
        """
        limit_perc = self.limit_p
        stop_perc = max(market.stop_distance_min, self.stop_p)

        # Spread constraint
        if market.bid - market.offer > self.max_spread:
            return TradeDirection.NONE, None, None

        # Compute mid price
        current_mid = Utils.midpoint(market.bid, market.offer)

        high_prices = datapoints["high"]
        low_prices = datapoints["low"]
        close_prices = datapoints["close"]
        ltv = datapoints["volume"]

        # Check dataset integrity
        array_len_check = []
        array_len_check.append(len(high_prices))
        array_len_check.append(len(low_prices))
        array_len_check.append(len(close_prices))
        array_len_check.append(len(ltv))
        if not all(x == array_len_check[0] for x in array_len_check):
            logging.error("Historic datapoints incomplete for {}".format(market.epic))
            return TradeDirection.NONE, None, None

        # compute weighted average and std deviation of prices using volume as weight
        low_prices = numpy.ma.asarray(low_prices)
        high_prices = numpy.ma.asarray(high_prices)
        ltv = numpy.ma.asarray(ltv)
        low_weighted_avg, low_weighted_std_dev = self.weighted_avg_and_std(
            low_prices, ltv
        )
        high_weighted_avg, high_weighted_std_dev = self.weighted_avg_and_std(
            high_prices, ltv
        )

        # The VWAP can be used similar to moving averages, where prices above
        # the VWAP reflect a bullish sentiment and prices below the VWAP
        # reflect a bearish sentiment. Traders may initiate short positions as
        # a stock price moves below VWAP for a given time period or initiate
        # long position as the price moves above VWAP

        tmp_high_weight_var = float(high_weighted_avg + high_weighted_std_dev)
        tmp_low_weight_var = float(low_weighted_avg + low_weighted_std_dev)
        # e.g
        # series = [0,0,0,2,0,0,0,-2,0,0,0,2,0,0,0,-2,0]

        maxtab_high, _mintab_high = self.peakdet(high_prices, 0.3)
        _maxtab_low, mintab_low = self.peakdet(low_prices, 0.3)

        # convert to array so can work on min/max
        mintab_low_a = array(mintab_low)[:, 1]
        maxtab_high_a = array(maxtab_high)[:, 1]

        xb = range(0, len(mintab_low_a))
        xc = range(0, len(maxtab_high_a))

        mintab_low_a_slope, mintab_low_a_intercept, mintab_low_a_lo_slope, mintab_low_a_hi_slope = stats.mstats.theilslopes(
            mintab_low_a, xb, 0.99
        )
        maxtab_high_a_slope, maxtab_high_a_intercept, maxtab_high_a_lo_slope, maxtab_high_a_hi_slope = stats.mstats.theilslopes(
            maxtab_high_a, xc, 0.99
        )

        peak_count_high = 0
        peak_count_low = 0
        # how may "peaks" are BELOW the threshold
        for a in mintab_low_a:
            if float(a) < float(tmp_low_weight_var):
                peak_count_low += 1

        # how may "peaks" are ABOVE the threshold
        for a in maxtab_high_a:
            if float(a) > float(tmp_high_weight_var):
                peak_count_high += 1

        additional_checks_sell = [
            int(peak_count_low) > int(peak_count_high),
            float(mintab_low_a_slope) < float(maxtab_high_a_slope),
        ]
        additional_checks_buy = [
            int(peak_count_high) > int(peak_count_low),
            float(maxtab_high_a_slope) > float(mintab_low_a_slope),
        ]

        sell_rules = [
            float(current_mid) >= float(numpy.max(maxtab_high_a)),
            all(additional_checks_sell),
        ]
        buy_rules = [
            float(current_mid) <= float(numpy.min(mintab_low_a)),
            all(additional_checks_buy),
        ]

        trade_direction = TradeDirection.NONE
        if any(buy_rules):
            trade_direction = TradeDirection.BUY
        elif any(sell_rules):
            trade_direction = TradeDirection.SELL

        if trade_direction is TradeDirection.NONE:
            return trade_direction, None, None

        logging.info("Strategy says: {} {}".format(trade_direction.name, market.id))

        ATR = self.calculate_stop_loss(close_prices, high_prices, low_prices)

        if trade_direction is TradeDirection.BUY:
            pip_limit = int(
                abs(float(max(high_prices)) - float(market.bid))
                * self.profit_indicator_multiplier
            )
            ce_stop = self.Chandelier_Exit_formula(
                trade_direction, ATR, min(low_prices)
            )
            stop_pips = str(int(abs(float(market.bid) - (ce_stop))))
        elif trade_direction is TradeDirection.SELL:
            pip_limit = int(
                abs(float(min(low_prices)) - float(market.bid))
                * self.profit_indicator_multiplier
            )
            ce_stop = self.Chandelier_Exit_formula(
                trade_direction, ATR, max(high_prices)
            )
        stop_pips = str(int(abs(float(market.bid) - (ce_stop))))

        esma_new_margin_req = int(Utils.percentage_of(self.ESMA_new_margin, market.bid))

        if int(esma_new_margin_req) > int(stop_pips):
            stop_pips = int(esma_new_margin_req)
        # is there a case for a 20% drop? ... Especially over 18 weeks or
        # so?
        if int(stop_pips) > int(esma_new_margin_req):
            stop_pips = int(esma_new_margin_req)
        if int(pip_limit) == 0:
            # not worth the trade
            trade_direction = TradeDirection.NONE
        if int(pip_limit) == 1:
            # not worth the trade
            trade_direction = TradeDirection.NONE
        if int(pip_limit) >= int(self.greed_indicator):
            pip_limit = int(self.greed_indicator - 1)
        if int(stop_pips) > int(self.too_high_margin):
            logging.warning("Junk data for {}".format(market.epic))
            return TradeDirection.NONE, None, None
        return trade_direction, pip_limit, stop_pips
コード例 #10
0
 def get_seconds_to_next_spin(self):
     """
     Calculate the amount of seconds to wait for between each strategy spin
     """
     # Run this strategy at market opening
     return Utils.get_seconds_to_market_opening(datetime.now())
コード例 #11
0
def test_humanize_time():
    assert Utils.humanize_time(3600) == "01:00:00"
    assert Utils.humanize_time(4800) == "01:20:00"
    assert Utils.humanize_time(4811) == "01:20:11"
コード例 #12
0
def test_is_between():
    mock = "10:10"
    assert Utils.is_between(mock, ("10:09", "10:11"))
    mock = "00:00"
    assert Utils.is_between(mock, ("23:59", "00:01"))
コード例 #13
0
def test_percentage():
    assert Utils.percentage(1, 100) == 1
    assert Utils.percentage(0, 100) == 0
    assert Utils.percentage(200, 100) == 200
コード例 #14
0
def test_percentage_of():
    assert Utils.percentage_of(1, 100) == 1
    assert Utils.percentage_of(0, 100) == 0
    assert Utils.percentage_of(1, 1) == 0.01
コード例 #15
0
def test_midpoint():
    assert Utils.midpoint(0, 10) == 5
    assert Utils.midpoint(-10, 10) == 0
    assert Utils.midpoint(10, -10) == 0
    assert Utils.midpoint(0, 0) == 0
    assert Utils.midpoint(1, 2) == 1.5