コード例 #1
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
コード例 #2
0
 def get_prices(self, market, interval, data_range):
     url = "{}/{}/{}/{}/{}".format(
         self.api_base_url,
         IG_API_URL.PRICES.value,
         market.epic,
         interval,
         data_range,
     )
     data = self._http_get(url)
     if "allowance" in data:
         remaining_allowance = data["allowance"]["remainingAllowance"]
         reset_time = Utils.humanize_time(
             int(data["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)))
     dates = []
     highs = []
     lows = []
     closes = []
     volumes = []
     for price in data["prices"]:
         dates.append(price["snapshotTimeUTC"])
         highs.append(price["highPrice"]["bid"])
         lows.append(price["lowPrice"]["bid"])
         closes.append(price["closePrice"]["bid"])
         volumes.append(int(price["lastTradedVolume"]))
     history = MarketHistory(market, dates, highs, lows, closes, volumes)
     return history
コード例 #3
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)
コード例 #4
0
def test_is_market_open():
    tp = TimeProvider()
    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 = tp.is_market_open(timezone)

    assert result == expected
コード例 #5
0
 def get_macd(self, market, interval, data_range):
     self._wait_before_call(self._config.get_yfinance_api_timeout())
     # Fetch prices with at least 26 data points
     prices = self.get_prices(market, interval, 30)
     data = Utils.macd_df_from_list(
         prices.dataframe[MarketHistory.CLOSE_COLUMN].values)
     # TODO use dates instead of index
     return MarketMACD(
         market,
         range(len(data)),
         data["MACD"].values,
         data["Signal"].values,
         data["Hist"].values,
     )
コード例 #6
0
ファイル: test_utils.py プロジェクト: amardahiya/TradingBot
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"
コード例 #7
0
ファイル: test_utils.py プロジェクト: amardahiya/TradingBot
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"))
コード例 #8
0
ファイル: test_utils.py プロジェクト: amardahiya/TradingBot
def test_percentage():
    assert Utils.percentage(1, 100) == 1
    assert Utils.percentage(0, 100) == 0
    assert Utils.percentage(200, 100) == 200
コード例 #9
0
ファイル: test_utils.py プロジェクト: amardahiya/TradingBot
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
コード例 #10
0
ファイル: test_utils.py プロジェクト: amardahiya/TradingBot
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
コード例 #11
0
 def _macd_dataframe(self, market, interval):
     prices = self.get_prices(market, "DAY", 26)
     if prices is None:
         return None
     return Utils.macd_df_from_list(
         prices.dataframe[MarketHistory.CLOSE_COLUMN].values)
コード例 #12
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.dataframe[MarketHistory.HIGH_COLUMN].values
        low_prices = datapoints.dataframe[MarketHistory.LOW_COLUMN].values
        close_prices = datapoints.dataframe[MarketHistory.CLOSE_COLUMN].values
        ltv = datapoints.dataframe[MarketHistory.VOLUME_COLUMN].values

        # 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