Ejemplo n.º 1
0
def find_supports(
    current_value,
    minute_history,
    now: datetime,
    range_type: StopRangeType = StopRangeType.LAST_100_MINUTES,
):
    # get low Series based on select time-range
    if range_type == StopRangeType.DAILY:
        series = (minute_history["low"][ts(
            now.replace(hour=9, minute=30, second=0, microsecond=0, tzinfo=est)
        ):].resample("5min").min())
    elif range_type == StopRangeType.LAST_100_MINUTES:
        series = minute_history["low"][-100:].dropna().resample("5min").min()
        series = series[ts(now).floor("1D"):]
    elif range_type == StopRangeType.LAST_2_HOURS:
        series = minute_history["low"][-120:].dropna().resample("5min").min()
        series = series[ts(now).floor("1D"):]
    elif range_type == StopRangeType.LAST_3_HOURS:
        series = minute_history["low"][-180:].dropna().resample("5min").min()
        series = series[ts(now).floor("1D"):]
    else:
        raise NotImplementedError(
            f"stop-range type {range_type} is not implemented")

    # find local minima
    diff = np.diff(series.values)
    low_index = np.where((diff[:-1] <= 0) & (diff[1:] > 0))[0] + 1
    if len(low_index) > 0:
        return [series[x] for x in low_index if series[x] < current_value]
    return None
Ejemplo n.º 2
0
def add_daily_vwap(minute_data: df, debug=False) -> bool:
    back_time = ts(config.market_open)

    try:
        back_time_index = minute_data["close"].index.get_loc(back_time,
                                                             method="nearest")
    except Exception as e:
        if debug:
            tlog(
                f"IndexError exception {e} in add_daily_vwap for {minute_data}"
            )
        return False

    minute_data["pv"] = minute_data.apply(
        lambda x: (x["close"] + x["high"] + x["low"]) / 3 * x["volume"],
        axis=1)
    minute_data["apv"] = minute_data["pv"][back_time_index:].cumsum()
    minute_data["av"] = minute_data["volume"][back_time_index:].cumsum()

    minute_data["average"] = minute_data["apv"] / minute_data["av"]
    minute_data["vwap"] = minute_data.apply(
        lambda x: (x["close"] + x["high"] + x["low"]) / 3, axis=1)

    # print(f"\n{tabulate(minute_data, headers='keys', tablefmt='psql')}")
    if debug:
        tlog(
            f"\n{tabulate(minute_data[-110:-100], headers='keys', tablefmt='psql')}"
        )
        tlog(
            f"\n{tabulate(minute_data[-10:], headers='keys', tablefmt='psql')}"
        )

    return True
Ejemplo n.º 3
0
async def find_resistances(
    symbol: str,
    strategy_name: str,
    current_value: float,
    minute_history: df,
    debug=False,
) -> Optional[List[float]]:
    """calculate supports"""

    est = pytz.timezone("America/New_York")
    back_time = ts(datetime.now(est)).to_pydatetime() - timedelta(days=3)
    back_time_index = minute_history["close"].index.get_loc(back_time,
                                                            method="nearest")

    series = (minute_history["close"][back_time_index:].dropna().between_time(
        "9:30", "16:00").resample("15min").max()).dropna()

    diff = np.diff(series.values)
    high_index = np.where((diff[:-1] >= 0) & (diff[1:] <= 0))[0] + 1
    if len(high_index) > 0:
        local_maximas = sorted(
            [series[i] for i in high_index if series[i] >= current_value])
        if len(local_maximas) > 0:
            if debug:
                tlog("find_resistances()")
                tlog(f"{minute_history}")
                tlog(f"{minute_history['close'][-1]}, {series}")
            # tlog(
            #    f"[{strategy_name}] find_resistances({symbol})={local_maximas}"
            # )
            return local_maximas

    return None
Ejemplo n.º 4
0
def find_stop(
    current_value,
    minute_history,
    now: datetime,
    range_type: StopRangeType = StopRangeType.LAST_100_MINUTES,
):
    if range_type in (StopRangeType.DATE_RANGE, StopRangeType.WEEKLY):
        raise NotImplementedError(
            f"stop-range type {range_type} is not implemented")

    if range_type == StopRangeType.DAILY:
        series = (minute_history["low"][ts(
            now.replace(hour=9, minute=30, second=0, microsecond=0, tzinfo=est)
        ):].dropna().resample("5min").min())
    else:
        series = minute_history["low"][-100:].dropna().resample("5min").min()
        series = series[ts(now).floor("1D"):]

    diff = np.diff(series.values)
    low_index = np.where((diff[:-1] <= 0) & (diff[1:] > 0))[0] + 1
    if len(low_index) > 0:
        return series[low_index[-1]]  # - max(0.05, current_value * 0.02)
    return None  # current_value * config.default_stop
Ejemplo n.º 5
0
    async def run(
        self,
        symbol: str,
        position: int,
        minute_history: df,
        now: datetime,
        portfolio_value: float = None,
        trading_api: tradeapi = None,
        debug: bool = False,
        backtesting: bool = False,
    ) -> Tuple[bool, Dict]:
        data = minute_history.iloc[-1]
        prev_min = minute_history.iloc[-2]

        morning_rush = (True if (now - config.market_open).seconds // 60 < 30
                        else False)

        if (await super().is_buy_time(now) and not position
                and not await self.should_cool_down(symbol, now)):
            # Check for buy signals
            lbound = config.market_open
            ubound = lbound + timedelta(minutes=15)

            if debug:
                tlog(f"15 schedule {lbound}/{ubound}")
            try:
                high_15m = minute_history[lbound:ubound]["high"].max(
                )  # type: ignore
                if debug:
                    tlog(f"{minute_history[lbound:ubound]}")  # type: ignore
            except Exception as e:
                # Because we're aggregating on the fly, sometimes the datetime
                # index can get messy until it's healed by the minute bars
                tlog(
                    f"[{self.name}] error aggregation {e} - maybe should use nearest?"
                )
                return False, {}

            if debug:
                tlog(f"15 minutes high:{high_15m}")

            # Get the change since yesterday's market close
            if data.close > high_15m or (
                    hasattr(config, "bypass_market_schedule")
                    and config.bypass_market_schedule
            ):  # and volume_today[symbol] > 30000:
                if debug:
                    tlog(
                        f"[{now}]{symbol} {data.close} above 15 minute high {high_15m}"
                    )

                last_30_max_close = minute_history[-30:]["close"].max()
                last_30_min_close = minute_history[-30:]["close"].min()

                if ((now - config.market_open).seconds // 60 > 90 and
                    (last_30_max_close - last_30_min_close) / last_30_min_close
                        > 0.1
                        and (not hasattr(config, "bypass_market_schedule")
                             or not config.bypass_market_schedule)):
                    tlog(
                        f"[{self.name}][{now}] too sharp {symbol} increase in last 30 minutes, can't trust MACD, cool down for 15 minutes"
                    )
                    cool_down[symbol] = now.replace(
                        second=0, microsecond=0) + timedelta(minutes=15)
                    return False, {}

                serie = (minute_history["close"].dropna().between_time(
                    "9:30", "16:00"))

                if data.vwap:
                    serie[-1] = data.vwap
                macds = MACD(serie)
                sell_macds = MACD(serie, 13, 21)
                macd1 = macds[0]
                macd_signal = macds[1]
                round_factor = (2 if macd1[-1] >= 0.1 or macd_signal[-1] >= 0.1
                                else 3)

                minute_shift = 0 if morning_rush or debug else -1

                if debug:
                    if macd1[-1 + minute_shift].round(round_factor) > 0:
                        tlog(f"[{now}]{symbol} MACD > 0")
                    if (macd1[-3 + minute_shift].round(round_factor) <
                            macd1[-2 + minute_shift].round(round_factor) <
                            macd1[-1 + minute_shift].round(round_factor)):
                        tlog(f"[{now}]{symbol} MACD trending")
                    else:
                        tlog(
                            f"[{now}]{symbol} MACD NOT trending -> failed {macd1[-3 + minute_shift].round(round_factor)} {macd1[-2 + minute_shift].round(round_factor)} {macd1[-1 + minute_shift].round(round_factor)}"
                        )
                    if (macd1[-1 + minute_shift] >
                            macd_signal[-1 + minute_shift]):
                        tlog(f"[{now}]{symbol} MACD above signal")
                    else:
                        tlog(f"[{now}]{symbol} MACD BELOW signal -> failed")
                    if data.close >= data.open:
                        tlog(f"[{now}]{symbol} above open")
                    else:
                        tlog(
                            f"[{now}]{symbol} close {data.close} BELOW open {data.open} -> failed"
                        )
                if (macd1[-1 + minute_shift].round(round_factor) > 0
                        and macd1[-3 + minute_shift].round(round_factor) <
                        macd1[-2 + minute_shift].round(round_factor) <
                        macd1[-1 + minute_shift].round(round_factor)
                        and macd1[-1 + minute_shift] >
                        macd_signal[-1 + minute_shift]
                        and sell_macds[0][-1 + minute_shift] > 0
                        and data.vwap > data.open
                        and data.close > prev_min.close
                        and data.close > data.open):
                    if symbol in voi and voi[symbol][-1] < 0:
                        tlog(
                            f"[{self.name}][{now}] Don't buy {symbol} on negative voi {voi[symbol]}"
                        )
                        return False, {}
                    if symbol in voi and voi[symbol][-1] < voi[symbol][-2]:
                        tlog(
                            f"[{self.name}][{now}] Don't buy {symbol} if voi not trending up {voi[symbol]}"
                        )
                        return False, {}

                    if symbol in voi:
                        tlog(
                            f"[{self.name}][{now}] {symbol} voi {voi[symbol]}")
                    tlog(
                        f"[{self.name}][{now}] MACD(12,26) for {symbol} trending up!, MACD(13,21) trending up and above signals"
                    )

                    if False:  # not morning_rush:
                        back_time = ts(config.market_open)
                        back_time_index = minute_history[
                            "close"].index.get_loc(back_time, method="nearest")
                        close = (
                            minute_history["close"]
                            [back_time_index:].dropna().between_time(
                                "9:30",
                                "16:00").resample("5min").last()).dropna()
                        open = (
                            minute_history["open"]
                            [back_time_index:].dropna().between_time(
                                "9:30",
                                "16:00").resample("5min").first()).dropna()
                        if close[-1] > open[-1]:
                            tlog(
                                f"[{now}] {symbol} confirmed 5-min candle bull"
                            )

                            if debug:
                                tlog(
                                    f"[{now}] {symbol} open={open[-5:]} close={close[-5:]}"
                                )
                        else:
                            tlog(
                                f"[{now}] {symbol} did not confirm 5-min candle bull {close[-5:]} {open[-5:]}"
                            )
                            return False, {}

                    macd2 = MACD(serie, 40, 60)[0]
                    # await asyncio.sleep(0)
                    if (macd2[-1 + minute_shift] >= 0
                            and np.diff(macd2)[-1 + minute_shift] >= 0):
                        tlog(
                            f"[{self.name}][{now}] MACD(40,60) for {symbol} trending up!"
                        )
                        # check RSI does not indicate overbought
                        rsi = RSI(serie, 14)

                        if not (rsi[-1 + minute_shift] > rsi[-2 + minute_shift]
                                > rsi[-3 + minute_shift]):
                            tlog(
                                f"[{self.name}][{now}] {symbol} RSI counter MACD trend ({rsi[-1+minute_shift]},{rsi[-2+minute_shift]},{rsi[-3+minute_shift]})"
                            )
                            # return False, {}

                        # await asyncio.sleep(0)
                        tlog(
                            f"[{self.name}][{now}] {symbol} RSI={round(rsi[-1+minute_shift], 2)}"
                        )

                        rsi_limit = 71 if not morning_rush else 80
                        if rsi[-1 + minute_shift] <= rsi_limit:
                            tlog(
                                f"[{self.name}][{now}] {symbol} RSI {round(rsi[-1+minute_shift], 2)} <= {rsi_limit}"
                            )

                            enforce_resistance = (
                                False  # (True if not morning_rush else False)
                            )

                            if enforce_resistance:
                                resistances = await find_resistances(
                                    symbol,
                                    self.name,
                                    min(
                                        data.low, prev_min.close
                                    ),  # data.close if not data.vwap else data.vwap,
                                    minute_history,
                                    debug,
                                )

                                supports = await find_supports(
                                    symbol,
                                    self.name,
                                    min(
                                        data.low, prev_min.close
                                    ),  # data.close if not data.vwap else data.vwap,
                                    minute_history,
                                    debug,
                                )
                                if resistances is None or resistances == []:
                                    tlog(
                                        f"[{self.name}] no resistance for {symbol} -> skip buy"
                                    )
                                    cool_down[symbol] = now.replace(
                                        second=0, microsecond=0)
                                    return False, {}
                                if supports is None or supports == []:
                                    tlog(
                                        f"[{self.name}] no supports for {symbol} -> skip buy"
                                    )
                                    cool_down[symbol] = now.replace(
                                        second=0, microsecond=0)
                                    return False, {}

                                next_resistance = None
                                for potential_resistance in resistances:
                                    if potential_resistance > data.close:
                                        next_resistance = potential_resistance
                                        break

                                if not next_resistance:
                                    tlog(
                                        f"[{self.name}] did not find resistance above {data.close}"
                                    )
                                    return False, {}

                                if next_resistance - data.close < 0.05:
                                    tlog(
                                        f"[{self.name}] {symbol} at price {data.close} too close to resistance {next_resistance}"
                                    )
                                    return False, {}
                                if data.close - supports[-1] < 0.05:
                                    tlog(
                                        f"[{self.name}] {symbol} at price {data.close} too close to support {supports[-1]} -> trend not established yet"
                                    )
                                    return False, {}
                                if (next_resistance - data.close) / (
                                        data.close - supports[-1]) < 0.8:
                                    tlog(
                                        f"[{self.name}] {symbol} at price {data.close} missed entry point between support {supports[-1]} and resistance {next_resistance}"
                                    )
                                    cool_down[symbol] = now.replace(
                                        second=0, microsecond=0)
                                    return False, {}

                                tlog(
                                    f"[{self.name}] {symbol} at price {data.close} found entry point between support {supports[-1]} and resistance {next_resistance}"
                                )
                                # Stock has passed all checks; figure out how much to buy
                                stop_price = find_stop(
                                    data.close if not data.vwap else data.vwap,
                                    minute_history,
                                    now,
                                )
                                stop_prices[symbol] = min(
                                    stop_price, supports[-1] - 0.05)
                                target_prices[symbol] = (
                                    data.close +
                                    (data.close - stop_prices[symbol]) * 2)
                                symbol_resistance[symbol] = next_resistance

                                if next_resistance - data.vwap < 0.05:
                                    tlog(
                                        f"[{self.name}] {symbol} at price {data.close} too close to resistance {next_resistance}"
                                    )
                                    return False, {}
                                # if data.vwap - support < 0.05:
                                #    tlog(
                                #        f"[{self.name}] {symbol} at price {data.close} too close to support {support} -> trend not established yet"
                                #    )
                                #    return False, {}
                                if (next_resistance - data.vwap) / (
                                        data.vwap - stop_prices[symbol]) < 0.8:
                                    tlog(
                                        f"[{self.name}] {symbol} at price {data.close} missed entry point between support {stop_prices[symbol] } and resistance {next_resistance}"
                                    )
                                    cool_down[symbol] = now.replace(
                                        second=0, microsecond=0)
                                    return False, {}

                                tlog(
                                    f"[{self.name}] {symbol} at price {data.close} found entry point between support {stop_prices[symbol]} and resistance {next_resistance}"
                                )

                                resistance = next_resistance
                                support = target_prices[symbol]
                            else:
                                stop_price = find_stop(
                                    data.close if not data.vwap else data.vwap,
                                    minute_history,
                                    now,
                                )
                                target_price = (3 * (data.close - stop_price) +
                                                data.close)
                                target_prices[symbol] = target_price
                                stop_prices[symbol] = stop_price
                                resistance = target_price
                                support = stop_price
                                symbol_resistance[symbol] = target_price

                            if portfolio_value is None:
                                if trading_api:
                                    portfolio_value = float(
                                        trading_api.get_account(
                                        ).portfolio_value)
                                else:
                                    raise Exception(
                                        "MomentumLong.run(): both portfolio_value and trading_api can't be None"
                                    )

                            shares_to_buy = (
                                portfolio_value * config.risk //
                                (data.close - stop_prices[symbol]))
                            if not shares_to_buy:
                                shares_to_buy = 1
                            shares_to_buy -= position
                            if shares_to_buy > 0:
                                buy_price = max(data.close, data.vwap)
                                tlog(
                                    f"[{self.name}] Submitting buy for {shares_to_buy} shares of {symbol} at {buy_price} target {target_prices[symbol]} stop {stop_prices[symbol]}"
                                )

                                # await asyncio.sleep(0)
                                buy_indicators[symbol] = {
                                    "rsi":
                                    rsi[-1 + minute_shift].tolist(),
                                    "macd":
                                    macd1[-5 + minute_shift:].tolist(),
                                    "macd_signal":
                                    macd_signal[-5 + minute_shift:].tolist(),
                                    "slow macd":
                                    macd2[-5 + minute_shift:].tolist(),
                                    "sell_macd":
                                    sell_macds[0][-5 + minute_shift:].tolist(),
                                    "sell_macd_signal":
                                    sell_macds[1][-5 + minute_shift:].tolist(),
                                    "resistances": [resistance],
                                    "supports": [support],
                                    "vwap":
                                    data.vwap,
                                    "avg":
                                    data.average,
                                    "position_ratio":
                                    str(
                                        round(
                                            (resistance - data.vwap) /
                                            (data.vwap - support),
                                            2,
                                        )),
                                }
                                if symbol in voi:
                                    buy_indicators[symbol]["voi"] = voi[symbol]

                                return (
                                    True,
                                    {
                                        "side": "buy",
                                        "qty": str(shares_to_buy),
                                        "type": "limit",
                                        "limit_price": str(buy_price),
                                    } if not morning_rush else {
                                        "side": "buy",
                                        "qty": str(shares_to_buy),
                                        "type": "market",
                                    },
                                )

                    else:
                        tlog(f"[{self.name}] failed MACD(40,60) for {symbol}!")

        if (await super().is_sell_time(now) and position > 0
                and symbol in latest_cost_basis
                and last_used_strategy[symbol].name == self.name):
            if open_orders.get(symbol) is not None:
                tlog(
                    f"momentum_long: open order for {symbol} exists, skipping")
                return False, {}

            # Check for liquidation signals
            # Sell for a loss if it's fallen below our stop price
            # Sell for a loss if it's below our cost basis and MACD < 0
            # Sell for a profit if it's above our target price
            macds = MACD(
                minute_history["close"].dropna().between_time("9:30", "16:00"),
                13,
                21,
            )
            # await asyncio.sleep(0)
            macd = macds[0]
            macd_signal = macds[1]
            rsi = RSI(
                minute_history["close"].dropna().between_time("9:30", "16:00"),
                14,
            )
            movement = (data.close - latest_scalp_basis[symbol]
                        ) / latest_scalp_basis[symbol]
            macd_val = macd[-1]
            macd_signal_val = macd_signal[-1]

            round_factor = (2 if macd_val >= 0.1 or macd_signal_val >= 0.1 else
                            3)
            # await asyncio.sleep(0)
            if (symbol_resistance and symbol in symbol_resistance
                    and symbol_resistance[symbol]):
                scalp_threshold = (symbol_resistance[symbol] +
                                   latest_scalp_basis[symbol]) / 2.0
            else:
                scalp_threshold = (target_prices[symbol] +
                                   latest_scalp_basis[symbol]) / 2.0
            bail_threshold = (latest_scalp_basis[symbol] +
                              scalp_threshold) / 2.0
            macd_below_signal = round(macd_val, round_factor) < round(
                macd_signal_val, round_factor)
            open_rush = (False if (now - config.market_open).seconds // 60 > 45
                         else True)
            bail_out = (
                # movement > min(0.02, movement_threshold) and macd_below_signal
                (movement > 0.01 or data.vwap > bail_threshold
                 )  # or open_rush)
                and macd_below_signal and
                round(macd[-1], round_factor) < round(macd[-2], round_factor))
            bail_on_rsi = (movement > 0.01
                           or data.vwap > bail_threshold) and rsi[-2] < rsi[-3]

            if debug and not bail_out:
                tlog(
                    f"[{now}]{symbol} don't bail: data.vwap={data.vwap} bail_threshold={bail_threshold} macd_below_signal={macd_below_signal} macd[-1]={ macd[-1]} macd[-2]={macd[-2]}"
                )

            scalp = (movement > 0.02 or data.vwap > scalp_threshold) and (
                symbol not in voi
                or voi[symbol][-1] < voi[symbol][-2] < voi[symbol][-3])
            below_cost_base = data.vwap < latest_cost_basis[symbol]
            rsi_limit = 79 if not morning_rush else 85
            to_sell = False
            partial_sell = False
            limit_sell = False
            sell_reasons = []
            if data.close <= stop_prices[symbol]:
                to_sell = True
                sell_reasons.append("stopped")
            elif (below_cost_base and round(macd_val, 2) < 0
                  and rsi[-1] < rsi[-2]
                  and round(macd[-1], 2) < round(macd[-2], 2)):
                to_sell = True
                sell_reasons.append(
                    "below cost & macd negative & RSI trending down")
            elif data.close >= target_prices[symbol] and macd[-1] <= 0:
                to_sell = True
                sell_reasons.append("above target & macd negative")
            elif rsi[-1] >= rsi_limit:
                to_sell = True
                sell_reasons.append("rsi max, cool-down for 5 minutes")
                cool_down[symbol] = now.replace(
                    second=0, microsecond=0) + timedelta(minutes=5)
            elif bail_out:
                to_sell = True
                sell_reasons.append("bail")
            elif bail_on_rsi:
                to_sell = True
                sell_reasons.append("bail_on_rsi")
            elif scalp:
                partial_sell = True
                to_sell = True
                sell_reasons.append("scale-out")
            elif (symbol in voi and voi[symbol][-1] < 0
                  and voi[symbol][-1] < voi[symbol][-2] < voi[symbol][-3]):
                tlog(f"[{now}] {symbol} bail-on-voi identified but not acted")
                # to_sell = True
                # sell_reasons.append("bail on voi")
                # limit_sell = True

            # Check patterns
            if debug:
                tlog(
                    f"[{now}] {symbol} min-2 = {minute_history.iloc[-2].open} {minute_history.iloc[-2].high}, {minute_history.iloc[-2].low}, {minute_history.iloc[-2].close}"
                )

            if self.check_patterns:
                if (now - buy_time[symbol] > timedelta(minutes=1)
                        and gravestone_doji(
                            prev_min.open,
                            prev_min.high,
                            prev_min.low,
                            prev_min.close,
                        ) and data.close < data.open and data.vwap < data.open
                        and prev_min.close > latest_cost_basis[symbol]):
                    tlog(
                        f"[{now}]{symbol} identified gravestone doji {prev_min.open, prev_min.high, prev_min.low, prev_min.close}"
                    )
                    to_sell = True
                    partial_sell = False
                    sell_reasons.append("gravestone_doji")

                elif (now - buy_time[symbol] > timedelta(minutes=2)
                      and spinning_top_bearish_followup(
                          (
                              minute_history.iloc[-3].open,
                              minute_history.iloc[-3].high,
                              minute_history.iloc[-3].low,
                              minute_history.iloc[-3].close,
                          ),
                          (
                              minute_history.iloc[-2].open,
                              minute_history.iloc[-2].high,
                              minute_history.iloc[-2].low,
                              minute_history.iloc[-2].close,
                          ),
                      ) and data.vwap < data.open):
                    tlog(
                        f"[{now}] {symbol} identified bullish spinning top followed by bearish candle {(minute_history.iloc[-3].open, minute_history.iloc[-3].high,minute_history.iloc[-3].low, minute_history.iloc[-3].close), (minute_history.iloc[-2].open, minute_history.iloc[-2].high, minute_history.iloc[-2].low, minute_history.iloc[-2].close)}"
                    )
                    to_sell = True
                    partial_sell = False
                    sell_reasons.append("bull_spinning_top_bearish_followup")

                elif (now - buy_time[symbol] > timedelta(minutes=2)
                      and bullish_candle_followed_by_dragonfly(
                          (
                              minute_history.iloc[-3].open,
                              minute_history.iloc[-3].high,
                              minute_history.iloc[-3].low,
                              minute_history.iloc[-3].close,
                          ),
                          (
                              minute_history.iloc[-2].open,
                              minute_history.iloc[-2].high,
                              minute_history.iloc[-2].low,
                              minute_history.iloc[-2].close,
                          ),
                      ) and data.vwap < data.open):
                    tlog(
                        f"[{now}] {symbol} identified bullish candle followed by dragonfly candle {(minute_history.iloc[-3].open, minute_history.iloc[-3].high,minute_history.iloc[-3].low, minute_history.iloc[-3].close), (minute_history.iloc[-2].open, minute_history.iloc[-2].high, minute_history.iloc[-2].low, minute_history.iloc[-2].close)}"
                    )
                    to_sell = True
                    partial_sell = False
                    sell_reasons.append("bullish_candle_followed_by_dragonfly")
                elif (now - buy_time[symbol] > timedelta(minutes=2)
                      and morning_rush and bearish_candle(
                          minute_history.iloc[-3].open,
                          minute_history.iloc[-3].high,
                          minute_history.iloc[-3].low,
                          minute_history.iloc[-3].close,
                      ) and bearish_candle(
                          minute_history.iloc[-2].open,
                          minute_history.iloc[-2].high,
                          minute_history.iloc[-2].low,
                          minute_history.iloc[-2].close,
                      ) and minute_history.iloc[-2].close <
                      minute_history.iloc[-3].close):
                    tlog(
                        f"[{now}] {symbol} identified two consequtive bullish candles during morning rush{(minute_history.iloc[-3].open, minute_history.iloc[-3].high, minute_history.iloc[-3].low, minute_history.iloc[-3].close), (minute_history.iloc[-2].open, minute_history.iloc[-2].high, minute_history.iloc[-2].low, minute_history.iloc[-2].close)}"
                    )
                    # to_sell = True
                    # partial_sell = False
                    # sell_reasons.append("two_bears_in_the_morning")

            if to_sell:
                close = minute_history["close"][-10:].dropna()
                open = minute_history["open"][-10:].dropna()
                high = minute_history["high"][-10:].dropna()
                low = minute_history["low"][-10:].dropna()

                patterns: Dict[ts, Dict[int, List[str]]] = {}
                pattern_functions = talib.get_function_groups(
                )["Pattern Recognition"]
                for pattern in pattern_functions:
                    pattern_value = getattr(talib, pattern)(open, high, low,
                                                            close)
                    result = pattern_value.to_numpy().nonzero()
                    if result[0].size > 0:
                        for timestamp, value in pattern_value.iloc[
                                result].items():
                            t = ts(timestamp)
                            if t not in patterns:
                                patterns[t] = {}
                            if value not in patterns[t]:
                                patterns[t][value] = [pattern]
                            else:
                                patterns[t][value].append(pattern)
                candle_s = Series(patterns)
                candle_s = candle_s.sort_index()

                sell_indicators[symbol] = {
                    "rsi": rsi[-3:].tolist(),
                    "movement": movement,
                    "sell_macd": macd[-5:].tolist(),
                    "sell_macd_signal": macd_signal[-5:].tolist(),
                    "vwap": data.vwap,
                    "avg": data.average,
                    "reasons":
                    " AND ".join([str(elem) for elem in sell_reasons]),
                    "patterns":
                    candle_s.to_json() if candle_s.size > 0 else None,
                }

                if symbol in voi:
                    sell_indicators[symbol]["voi"] = voi[symbol]

                if not partial_sell:

                    if not limit_sell:
                        tlog(
                            f"[{self.name}] Submitting sell for {position} shares of {symbol} at market"
                        )
                        return (
                            True,
                            {
                                "side": "sell",
                                "qty": str(position),
                                "type": "market",
                            },
                        )
                    else:
                        tlog(
                            f"[{self.name}] Submitting sell for {position} shares of {symbol} at {data.close}"
                        )
                        return (
                            True,
                            {
                                "side": "sell",
                                "qty": str(position),
                                "type": "limit",
                                "limit_price": str(data.close),
                            },
                        )
                else:
                    qty = int(position / 2) if position > 1 else 1
                    tlog(
                        f"[{self.name}] Submitting sell for {str(qty)} shares of {symbol} at limit of {data.close}"
                    )
                    return (
                        True,
                        {
                            "side": "sell",
                            "qty": str(qty),
                            "type": "limit",
                            "limit_price": str(data.close),
                        },
                    )

        return False, {}
Ejemplo n.º 6
0
    async def run(
        self,
        symbol: str,
        position: int,
        minute_history: df,
        now: datetime,
        portfolio_value: float = None,
        trading_api: tradeapi = None,
        debug: bool = False,
        backtesting: bool = False,
    ) -> Tuple[bool, Dict]:
        data = minute_history.iloc[-1]
        prev_minute = minute_history.iloc[-2]
        prev_2minutes = minute_history.iloc[-3]

        if await self.is_buy_time(now) and not position:
            # Check for buy signals

            back_time = ts(config.market_open)
            back_time_index = minute_history["close"].index.get_loc(
                back_time, method="nearest")
            close = (minute_history["close"]
                     [back_time_index:-1].dropna().between_time(
                         "9:30", "16:00").resample("5min").last()).dropna()
            open = (minute_history["open"]
                    [back_time_index:-1].dropna().between_time(
                        "9:30", "16:00").resample("5min").first()).dropna()
            high = (minute_history["high"]
                    [back_time_index:-1].dropna().between_time(
                        "9:30", "16:00").resample("5min").max()).dropna()
            low = (minute_history["low"]
                   [back_time_index:-1].dropna().between_time(
                       "9:30", "16:00").resample("5min").min()).dropna()
            volume = (minute_history["volume"]
                      [back_time_index:-1].dropna().between_time(
                          "9:30", "16:00").resample("5min").sum()).dropna()

            _df = concat(
                [
                    open.rename("open"),
                    high.rename("high"),
                    low.rename("low"),
                    close.rename("close"),
                    volume.rename("volume"),
                ],
                axis=1,
            )
            if not add_daily_vwap(_df):
                tlog(f"[{now}]failed add_daily_vwap")
                return False, {}

            if debug:
                tlog(
                    f"\n[{now}]{symbol} {tabulate(_df[-10:], headers='keys', tablefmt='psql')}"
                )
            vwap_series = _df["average"]

            if debug:
                tlog(
                    f"[{now}] {symbol} close:{round(data.close,2)} vwap:{round(vwap_series[-1],2)}"
                )

            if len(vwap_series) < 3:
                tlog(f"[{now}]{symbol}: missing vwap values {vwap_series}")
                return False, {}

            if close[-2] > vwap_series[-2] and close[-1] < vwap_series[-1]:
                down_cross[symbol] = vwap_series.index[-1].to_pydatetime()
                tlog(
                    f"[{now}] {symbol} down-crossing on 5-min bars at {down_cross[symbol]}"
                )
                return False, {}

            if (close[-2] > vwap_series[-2] and close[-3] < vwap_series[-3]
                    and data.close > prev_minute.close
                    and data.close > data.average):
                if not symbol in down_cross:
                    tlog(
                        f"[{now}] {symbol} did not find download crossing in the past 15 min"
                    )
                    return False, {}
                if minute_history.index[-1].to_pydatetime(
                ) - down_cross[symbol] > timedelta(minutes=30):
                    tlog(
                        f"[{now}] {symbol} down-crossing too far {down_cross[symbol]} from now"
                    )
                    return False, {}
                stop_price = find_stop(
                    data.close if not data.vwap else data.vwap,
                    minute_history,
                    now,
                )
                target = data.close + 0.05

                stop_prices[symbol] = stop_price
                target_prices[symbol] = target

                tlog(
                    f"{symbol} found conditions for VWAP-Scalp strategy now:{now}"
                )

                tlog(
                    f"\n{tabulate(minute_history[-10:], headers='keys', tablefmt='psql')}"
                )

                if portfolio_value is None:
                    if trading_api:
                        portfolio_value = float(
                            trading_api.get_account().portfolio_value)
                    else:
                        raise Exception(
                            "VWAPLong.run(): both portfolio_value and trading_api can't be None"
                        )

                shares_to_buy = (
                    portfolio_value * 20.0 * config.risk // data.close
                    # // (data.close - stop_prices[symbol])
                )
                if not shares_to_buy:
                    shares_to_buy = 1
                shares_to_buy -= position

                if shares_to_buy > 0:
                    tlog(
                        f"[{self.name}] Submitting buy for {shares_to_buy} shares of {symbol} at {data.close} target {target_prices[symbol]} stop {stop_prices[symbol]}"
                    )
                    buy_indicators[symbol] = {
                        "average": round(data.average, 2),
                        "vwap": round(data.vwap, 2),
                        "patterns": None,
                    }

                    return (
                        True,
                        {
                            "side": "buy",
                            "qty": str(shares_to_buy),
                            "type": "limit",
                            "limit_price": str(data.close + 0.01),
                        },
                    )

        elif (await super().is_sell_time(now) and position > 0
              and symbol in latest_cost_basis
              and last_used_strategy[symbol].name == self.name):
            if open_orders.get(symbol) is not None:
                tlog(f"vwap_scalp: open order for {symbol} exists, skipping")
                return False, {}

            to_sell = False
            to_sell_market = False
            if data.vwap <= data.average - 0.05:
                to_sell = True
                reason = "below VWAP"
                to_sell_market = True
            elif data.close >= target_prices[symbol]:
                to_sell = True
                reason = "vwap scalp"
            elif data.close >= target_prices[symbol]:
                to_sell = True
                reason = "vwap scalp"
            elif (prev_minute.close < prev_minute.open
                  and data.close < data.open):
                to_sell = True
                reason = "vwap scalp no bears"

            if to_sell:
                sell_indicators[symbol] = {
                    "reason": reason,
                    "average": data.average,
                    "vwap": data.vwap,
                }
                return (
                    True,
                    {
                        "side": "sell",
                        "qty": str(position),
                        "type": "market"
                    } if to_sell_market else {
                        "side": "sell",
                        "qty": str(position),
                        "type": "limit",
                        "limit_price": str(data.close),
                    },
                )

        return False, {}
Ejemplo n.º 7
0
def toTs(unixDate):
    """
    Convert a single unixTimestamp to UTC time.
    """
    return ts(unixDate, tz='utc', unit='s')
Ejemplo n.º 8
0
    async def run(
        self,
        symbol: str,
        shortable: bool,
        position: int,
        minute_history: df,
        now: datetime,
        portfolio_value: float = None,
        trading_api: tradeapi = None,
        debug: bool = False,
        backtesting: bool = False,
    ) -> Tuple[bool, Dict]:
        if not shortable:
            return False, {}

        data = minute_history.iloc[-1]

        if data.close > data.average:
            self.was_above_vwap[symbol] = True

        if (await super().is_buy_time(now) and not position
                and not open_orders.get(symbol, None)):
            day_start = ts(config.market_open)

            try:
                day_start_index = minute_history["close"].index.get_loc(
                    day_start, method="nearest")
            except Exception as e:
                tlog(
                    f"[ERROR]{self.name}[{now}]{symbol} can't load index for {day_start} w/ {e}"
                )
                return False, {}

            close = (minute_history["close"]
                     [day_start_index:-1].dropna().between_time(
                         "9:30", "16:00").resample("5min").last()).dropna()
            open = (minute_history["open"]
                    [day_start_index:-1].dropna().between_time(
                        "9:30", "16:00").resample("5min").first()).dropna()
            high = (minute_history["high"]
                    [day_start_index:-1].dropna().between_time(
                        "9:30", "16:00").resample("5min").max()).dropna()
            low = (minute_history["low"]
                   [day_start_index:-1].dropna().between_time(
                       "9:30", "16:00").resample("5min").min()).dropna()
            volume = (minute_history["volume"]
                      [day_start_index:-1].dropna().between_time(
                          "9:30", "16:00").resample("5min").sum()).dropna()

            df = concat(
                [
                    open.rename("open"),
                    high.rename("high"),
                    low.rename("low"),
                    close.rename("close"),
                    volume.rename("volume"),
                ],
                axis=1,
            )
            if not add_daily_vwap(df):
                tlog(f"[{now}]{symbol} failed in add_daily_vwap")
                return False, {}

            vwap_series = df["average"]

            if (data.close < vwap_series[-1] * 0.99
                    and self.was_above_vwap.get(symbol, False) and close[-1] <
                    open[-1] <= close[-2] < open[-2] <= close[-3] < open[-3]):

                stop_price = vwap_series[-1]
                target_price = data.close - 3 * (stop_price - data.close)

                stop_prices[symbol] = stop_price
                target_prices[symbol] = target_price

                if portfolio_value is None:
                    if trading_api:
                        retry = 3
                        while retry > 0:
                            try:
                                portfolio_value = float(
                                    trading_api.get_account().portfolio_value)
                                break
                            except ConnectionError as e:
                                tlog(
                                    f"[{symbol}][{now}[Error] get_account() failed w/ {e}, retrying {retry} more times"
                                )
                                await asyncio.sleep(0)
                                retry -= 1

                        if not portfolio_value:
                            tlog(
                                "f[{symbol}][{now}[Error] failed to get portfolio_value"
                            )
                            return False, {}
                    else:
                        raise Exception(
                            f"{self.name}: both portfolio_value and trading_api can't be None"
                        )

                shares_to_buy = (portfolio_value * config.risk //
                                 (data.close - stop_prices[symbol]))
                if not shares_to_buy:
                    shares_to_buy = 1

                buy_price = data.close
                tlog(
                    f"[{self.name}][{now}] Submitting buy short for {-shares_to_buy} shares of {symbol} at {buy_price} target {target_prices[symbol]} stop {stop_prices[symbol]}"
                )

                sell_indicators[symbol] = {
                    "vwap_series": vwap_series[-5:].tolist(),
                    "vwap": data.vwap,
                    "avg": data.average,
                }

                return (
                    True,
                    {
                        "side": "sell",
                        "qty": str(-shares_to_buy),
                        "type": "market",
                    },
                )

        if (await super().is_sell_time(now) and position
                and last_used_strategy[symbol].name == self.name
                and not open_orders.get(symbol)):
            day_start = ts(config.market_open)
            day_start_index = minute_history["close"].index.get_loc(
                day_start, method="nearest")
            close = (minute_history["close"]
                     [day_start_index:-1].dropna().between_time(
                         "9:30", "16:00").resample("5min").last()).dropna()
            to_sell: bool = False
            reason: str = ""

            if data.close >= stop_prices[symbol]:
                to_sell = True
                reason = "stopped"
            elif data.close <= target_prices[symbol]:
                to_sell = True
                reason = "target reached"
            elif close[-1] > close[-2] > close[-3] < close[-4]:
                to_sell = True
                reason = "reversing direction"

            if to_sell:
                buy_indicators[symbol] = {
                    "close_5m": close[-5:].tolist(),
                    "reason": reason,
                }

                tlog(
                    f"[{self.name}][{now}] Submitting sell short for {position} shares of {symbol} at market {data.close} with reason:{reason}"
                )
                return (
                    True,
                    {
                        "side": "buy",
                        "qty": str(-position),
                        "type": "market",
                    },
                )

        return False, {}
Ejemplo n.º 9
0
    async def run(
        self,
        symbol: str,
        position: int,
        minute_history: df,
        now: datetime,
        portfolio_value: float = None,
        trading_api: tradeapi = None,
        debug: bool = False,
        backtesting: bool = False,
    ) -> Tuple[bool, Dict]:
        data = minute_history.iloc[-1]
        prev_minute = minute_history.iloc[-2]
        if await self.is_buy_time(now) and not position:
            # Check for buy signals
            lbound = config.market_open
            ubound = lbound + timedelta(minutes=15)
            try:
                high_15m = minute_history[lbound:ubound]["high"].max(
                )  # type: ignore

                if data.vwap < high_15m:
                    return False, {}
            except Exception as e:
                # Because we're aggregating on the fly, sometimes the datetime
                # index can get messy until it's healed by the minute bars
                tlog(
                    f"[{self.name}] error aggregation {e} - maybe should use nearest?"
                )
                return False, {}

            back_time = ts(config.market_open)
            back_time_index = minute_history["close"].index.get_loc(
                back_time, method="nearest")
            close = (minute_history["close"]
                     [back_time_index:-1].dropna().between_time(
                         "9:30", "16:00").resample("5min").last()).dropna()
            open = (minute_history["open"]
                    [back_time_index:-1].dropna().between_time(
                        "9:30", "16:00").resample("5min").first()).dropna()
            high = (minute_history["high"]
                    [back_time_index:-1].dropna().between_time(
                        "9:30", "16:00").resample("5min").max()).dropna()
            low = (minute_history["low"]
                   [back_time_index:-1].dropna().between_time(
                       "9:30", "16:00").resample("5min").min()).dropna()
            volume = (minute_history["volume"]
                      [back_time_index:-1].dropna().between_time(
                          "9:30", "16:00").resample("5min").sum()).dropna()

            _df = concat(
                [
                    open.rename("open"),
                    high.rename("high"),
                    low.rename("low"),
                    close.rename("close"),
                    volume.rename("volume"),
                ],
                axis=1,
            )

            if not add_daily_vwap(_df):
                tlog(f"[{now}]failed add_daily_vwap")
                return False, {}

            if debug:
                tlog(
                    f"\n[{now}]{symbol} {tabulate(_df[-10:], headers='keys', tablefmt='psql')}"
                )
            vwap_series = _df["average"]

            if (
                    # data.vwap > close_series[-1] > close_series[-2]
                    # and round(data.average, 2) > round(vwap_series[-1], 2)
                    # and data.vwap > data.average
                    # and
                    data.low > data.average
                    and close[-1] > vwap_series[-1] > vwap_series[-2] > low[-2]
                    and close[-1] > high[-2]
                    and prev_minute.close > prev_minute.open
                    and data.close > data.open
                    and low[-2] < vwap_series[-2] - 0.2):
                stop_price = find_stop(
                    data.close if not data.vwap else data.vwap,
                    minute_history,
                    now,
                )
                # upperband, middleband, lowerband = BBANDS(
                #    minute_history["close"], timeperiod=20
                # )

                # stop_price = min(
                #    prev_minute.close,
                #    data.average - 0.01,
                #    lowerband[-1] - 0.03,
                # )
                target = (3 * (data.close - stop_price) + data.close
                          )  # upperband[-1]

                # if target - stop_price < 0.05:
                #    tlog(
                #        f"{symbol} target price {target} too close to stop price {stop_price}"
                #    )
                #    return False, {}
                # if target - data.close < 0.05:
                #    tlog(
                #        f"{symbol} target price {target} too close to close price {data.close}"
                #    )
                #    return False, {}

                stop_prices[symbol] = stop_price
                target_prices[symbol] = target

                patterns: Dict[ts, Dict[int, List[str]]] = {}
                pattern_functions = talib.get_function_groups(
                )["Pattern Recognition"]
                for pattern in pattern_functions:
                    pattern_value = getattr(talib, pattern)(open, high, low,
                                                            close)
                    result = pattern_value.to_numpy().nonzero()
                    if result[0].size > 0:
                        for timestamp, value in pattern_value.iloc[
                                result].items():
                            t = ts(timestamp)
                            if t not in patterns:
                                patterns[t] = {}
                            if value not in patterns[t]:
                                patterns[t][value] = [pattern]
                            else:
                                patterns[t][value].append(pattern)

                tlog(f"{symbol} found conditions for VWAP strategy now:{now}")
                candle_s = Series(patterns)
                candle_s = candle_s.sort_index()

                tlog(f"{symbol} 5-min VWAP {vwap_series}")
                tlog(f"{symbol} 5-min close values {close}")
                tlog(f"{symbol} {candle_s}")
                tlog(
                    f"\n{tabulate(minute_history[-10:], headers='keys', tablefmt='psql')}"
                )

                if candle_s.size > 0 and -100 in candle_s[-1]:
                    tlog(
                        f"{symbol} Bullish pattern does not exists -> should skip"
                    )
                    # return False, {}

                if portfolio_value is None:
                    if trading_api:
                        portfolio_value = float(
                            trading_api.get_account().portfolio_value)
                    else:
                        raise Exception(
                            "VWAPLong.run(): both portfolio_value and trading_api can't be None"
                        )

                shares_to_buy = (
                    portfolio_value * 20.0 * config.risk // data.close
                    # // (data.close - stop_prices[symbol])
                )
                print(
                    f"shares to buy {shares_to_buy} {data.close} {stop_prices[symbol]}"
                )
                if not shares_to_buy:
                    shares_to_buy = 1
                shares_to_buy -= position

                if shares_to_buy > 0:
                    tlog(
                        f"[{self.name}] Submitting buy for {shares_to_buy} shares of {symbol} at {data.close} target {target_prices[symbol]} stop {stop_prices[symbol]}"
                    )
                    buy_indicators[symbol] = {
                        # bbrand_lower": lowerband[-5:].tolist(),
                        # "bbrand_middle": middleband[-5:].tolist(),
                        # "bbrand_upper": upperband[-5:].tolist(),
                        "average": round(data.average, 2),
                        "vwap": round(data.vwap, 2),
                        "patterns": candle_s.to_json(),
                    }

                    return (
                        True,
                        {
                            "side": "buy",
                            "qty": str(shares_to_buy),
                            "type": "limit",
                            "limit_price": str(data.close),
                        },
                    )
            elif debug:
                tlog(f"[{now}]{symbol} failed vwap strategy")
                if not (data.low > data.average):
                    tlog(
                        f"[{now}]{symbol} failed data.low {data.low} > data.average {data.average}"
                    )
                if not (close[-1] > vwap_series[-1] > vwap_series[-2] >
                        low[-2]):
                    tlog(
                        f"[{now}]{symbol} failed close[-1] {close[-1]} > vwap_series[-1] {vwap_series[-1]} > vwap_series[-2]{ vwap_series[-2]} > low[-2] {low[-2]}"
                    )
                if not (prev_minute.close > prev_minute.open):
                    tlog(
                        f"[{now}]{symbol} failed prev_minute.close {prev_minute.close} > prev_minute.open {prev_minute.open}"
                    )
                if not (close[-1] > high[-2]):
                    tlog(
                        f"[{now}]{symbol} failed close[-1] {close[-1]} > high[-2] {high[-2]}"
                    )
                if not (data.close > data.open):
                    tlog(
                        f"[{now}]{symbol} failed data.close {data.close} > data.open {data.open}"
                    )
                if not low[-2] < vwap_series[-2] - 0.2:
                    tlog(
                        f"[{now}]{symbol} failed low[-2] {low[-2]} < vwap_series[-2] {vwap_series[-2] } - 0.2"
                    )

        elif (await super().is_sell_time(now) and position > 0
              and symbol in latest_cost_basis
              and last_used_strategy[symbol].name == self.name):
            if open_orders.get(symbol) is not None:
                tlog(f"vwap_long: open order for {symbol} exists, skipping")
                return False, {}

            if data.vwap <= data.average - 0.02:
                sell_indicators[symbol] = {
                    "reason": "below VWAP",
                    "average": data.average,
                    "vwap": data.vwap,
                }
                return (
                    True,
                    {
                        "side": "sell",
                        "qty": str(position),
                        "type": "market"
                    },
                )

        return False, {}