Oracle SQL 或 PL/SQL:如何仅在上升趋势或下降趋势结束时识别烛台形态并在列中设置标志?

Posted

技术标签:

【中文标题】Oracle SQL 或 PL/SQL:如何仅在上升趋势或下降趋势结束时识别烛台形态并在列中设置标志?【英文标题】:Oracle SQL or PL/SQL: How to identify candlestick pattern only in end of uptrend or downtrend and set a flag in column? 【发布时间】:2021-11-25 04:57:57 【问题描述】:

此问题和相关答案仅用于教育或学习目的。

这个问题与我的其他post 有很大不同,并且不重复。由于它造成了混乱并且正如@MT0 所建议的那样,我将其作为一个新问题发布在这里。

我有下表,我每天上传股票数据。

/* CREATE TABLE */
CREATE TABLE RAW_SOURCE(
  Stock  VARCHAR(100),
  Close_Date DATE,
  Open   NUMBER,
  High   NUMBER,
  Low    NUMBER,
  Close  NUMBER,
  Volume NUMBER
);

/* INSERT QUERY NO: 1 */
INSERT INTO RAW_SOURCE(Stock, Close_Date, Open, High, Low, Close, Volume)
VALUES
(
'XYZ', '01/01/2021', 40, 40.5, 38.5, 38.8, 83057
);

/* INSERT QUERY NO: 2 */
INSERT INTO RAW_SOURCE(Stock, Close_Date, Open, High, Low, Close, Volume)
VALUES
(
'XYZ', '02/01/2021', 39.2, 39.2, 37.2, 37.8, 181814
);

/* INSERT QUERY NO: 3 */
INSERT INTO RAW_SOURCE(Stock, Close_Date, Open, High, Low, Close, Volume)
VALUES
(
'XYZ', '03/01/2021', 38, 38.5, 36.5, 37, 117378
);

/* INSERT QUERY NO: 4 */
INSERT INTO RAW_SOURCE(Stock, Close_Date, Open, High, Low, Close, Volume)
VALUES
(
'XYZ', '04/01/2021', 36.5, 36.6, 35.6, 35.7, 93737
);

/* INSERT QUERY NO: 5 */
INSERT INTO RAW_SOURCE(Stock, Close_Date, Open, High, Low, Close, Volume)
VALUES
(
'XYZ', '05/01/2021', 35.35, 36.8, 35.1, 36.7, 169106
);

/* INSERT QUERY NO: 6 */
INSERT INTO RAW_SOURCE(Stock, Close_Date, Open, High, Low, Close, Volume)
VALUES
(
'XYZ', '06/01/2021', 36.5, 38.5, 36.5, 38, 123179
);

/* INSERT QUERY NO: 7 */
INSERT INTO RAW_SOURCE(Stock, Close_Date, Open, High, Low, Close, Volume)
VALUES
(
'XYZ', '07/01/2021', 37.5, 39.5, 37.3, 39.4, 282986
);

/* INSERT QUERY NO: 8 */
INSERT INTO RAW_SOURCE(Stock, Close_Date, Open, High, Low, Close, Volume)
VALUES
(
'XYZ', '08/01/2021', 39, 40.5, 38.5, 40, 117437
);

/* INSERT QUERY NO: 9 */
INSERT INTO RAW_SOURCE(Stock, Close_Date, Open, High, Low, Close, Volume)
VALUES
(
'XYZ', '09/01/2021', 39.7, 39.8, 39.3, 39.4, 873009
);

/* INSERT QUERY NO: 10 */
INSERT INTO RAW_SOURCE(Stock, Close_Date, Open, High, Low, Close, Volume)
VALUES
(
'XYZ', '10/01/2021', 39.2, 39.2, 37.2, 37.8, 62522
);

/* INSERT QUERY NO: 11 */
INSERT INTO RAW_SOURCE(Stock, Close_Date, Open, High, Low, Close, Volume)
VALUES
(
'XYZ', '11/01/2021', 38, 38.5, 36.5, 37, 114826
);

/* INSERT QUERY NO: 12 */
INSERT INTO RAW_SOURCE(Stock, Close_Date, Open, High, Low, Close, Volume)
VALUES
(
'XYZ', '12/01/2021', 36.5, 37.9, 36.3, 37.8, 281461
);

/* INSERT QUERY NO: 13 */
INSERT INTO RAW_SOURCE(Stock, Close_Date, Open, High, Low, Close, Volume)
VALUES
(
'XYZ', '13/01/2021', 37.5, 39.5, 37.3, 39.4, 77334
);

/* INSERT QUERY NO: 14 */
INSERT INTO RAW_SOURCE(Stock, Close_Date, Open, High, Low, Close, Volume)
VALUES
(
'XYZ', '14/01/2021', 39, 40.5, 38.5, 40, 321684
);

以下是一只股票“XYZ”的样本数据:

+-------+------------+-------+------+------+-------+--------+
| Stock | Close Date | Open  | High | Low  | Close | Volume |
+-------+------------+-------+------+------+-------+--------+
| XYZ   | 01-01-2021 |    40 | 40.5 | 38.5 |  38.8 |  83057 |
| XYZ   | 02-01-2021 |  39.2 | 39.2 | 37.2 |  37.8 | 181814 |
| XYZ   | 03-01-2021 |    38 | 38.5 | 36.5 |    37 | 117378 |
| XYZ   | 04-01-2021 |  36.5 | 36.6 | 35.6 |  35.7 |  93737 |
| XYZ   | 05-01-2021 | 35.35 | 36.8 | 35.1 |  36.7 | 169106 |
| XYZ   | 06-01-2021 |  36.5 | 38.5 | 36.5 |    38 | 123179 |
| XYZ   | 07-01-2021 |  37.5 | 39.5 | 37.3 |  39.4 | 282986 |
| XYZ   | 08-01-2021 |    39 | 40.5 | 38.5 |    40 | 117437 |
| XYZ   | 09-01-2021 |  39.7 | 39.8 | 39.3 |  39.4 | 873009 |
| XYZ   | 10-01-2021 |  39.2 | 39.2 | 37.2 |  37.8 |  62522 |
| XYZ   | 11-01-2021 |    38 | 38.5 | 36.5 |    37 | 114826 |
| XYZ   | 12-01-2021 |  36.5 | 37.9 | 36.3 |  37.8 | 281461 |
| XYZ   | 13-01-2021 |  37.5 | 39.5 | 37.3 |  39.4 |  77334 |
| XYZ   | 14-01-2021 |    39 | 40.5 | 38.5 |    40 | 321684 |
+-------+------------+-------+------+------+-------+--------+

在一段时间内,每个股票代码将有超过数千条记录,我想仅在上升/上升趋势的顶部或在下降/下降趋势的底部识别烛台形态,而不是横向(因为这将是误报)。以下是示例截图:

假设今天是 2021 年 1 月 12 日,以下是预期输出:

+-------+-------------------+------------+------------+--------------+--------+---------------+
| Stock | Consecutive Count | Start Date |  End Date  | Latest Close | Volume |    Pattern    |
+-------+-------------------+------------+------------+--------------+--------+---------------+
| XYZ   |                 3 | 09-01-2021 | 12-01-2021 |         37.8 | 281461 | Piercing Line |
+-------+-------------------+------------+------------+--------------+--------+---------------+

由于源表中将包含许多其他股票,因此如果发现任何模式,希望也显示 2021 年 1 月 12 日其他股票的结果。 我觉得这是非常具有挑战性和复杂的逻辑。因此在这里寻求帮助。提前致谢。

更新:谢谢@JustinCave 下面是计算公式: 对于看涨吞没

O1 > C1 and C > O and C > H1 and O < L1
where,

O1 = Previous day Open price  
C1 = Previous day Close price  
C  = Today's Close price  
O  = Today's Open price  
H1 = Previous day High price  
L1 = Previous day Low price 

对于看跌孕产

(O1 < C1) and (O > C) and (O < C1) and (C > O1) and (H < H1) and (L > L1)
where,
    O1 = Previous day Open price  
    C1 = Previous day Close price  
    C  = Today's Close price  
    O  = Today's Open price  
    H1 = Previous day High price  
    L1 = Previous day Low price 
    H  = Today's High price  
    L  = Today's Low price 

对于穿刺线

(O < C) and (O1 > C1) and (C > (C1 + O1)/2) and (O < C1) and (C < O1)
    where,
        O1 = Previous day Open price  
        C1 = Previous day Close price  
        C  = Today's Close price  
        O  = Today's Open price 

【问题讨论】:

这似乎是一个可以通过MATCH_RECOGNIZE 解决的问题。看看这个 Oracle Deep Dive 解释如何使用它(他们使用的例子实际上是股票模式)。 (Link) 如果您不想适应@MT0 在其他问题中为您提供的match_recognize 逻辑,您至少需要指定您想要使用的确切逻辑。例如,什么构成“穿透线”而不是“看涨吞没”? 谢谢@Del。我要通过链接。 谢谢@JustinCave。我已经更新了 OP 中的公式。 【参考方案1】:

MATCH_RECOGNIZE 中的模式与正则表达式的工作方式相似;你想要这样的东西:

(注意:你的PIERCING_LINE 公式没有给出预期的输出,所以我假设你想要C &gt; (C1 + O1)/2 而不是C &gt; C1 + (O1/2)。)

SELECT *
FROM   raw_source
MATCH_RECOGNIZE (
  PARTITION BY stock
  ORDER BY Close_Date
  MEASURES
    CLASSIFIER() AS pttrn
  ALL ROWS PER MATCH
  PATTERN (
    ^initial_value
    |
    down+ (bullish_engulfing | piercing_line | $)
    |
    up+ (bearish_harami | $)
    |
    other
  )
  DEFINE
    down AS
          PREV(open) > open
      AND PREV(close) > close
      AND PREV(open) > PREV(close)
      AND open > close,
    up AS
          PREV(open) < open
      AND PREV(close) < close
      AND PREV(open) < PREV(close)
      AND open < close,
    bullish_engulfing AS
      -- O1 > C1 and C > O and C > H1 and O < L1
          PREV(open) > PREV(close)
      AND close > open
      AND close > PREV(high)
      AND open  < PREV(low),
    bearish_harami AS
      -- O1 < C1 and O > C and O < C1 and C > O1 and H < H1 and L > L1
          PREV(open) < PREV(close)
      AND open > close
      AND open < PREV(close)
      AND close > PREV(open)
      AND high < PREV(high)
      AND low > PREV(low),
    piercing_line AS
      -- O < C and O1 > C1 and C > (C1 + O1)/2 and O < C1 and C < O1
          open < close
      AND PREV(open) > PREV(close)
      AND close > (PREV(close) + PREV(open))/2
      AND open < PREV(close)
      AND close < PREV(open)
)

哪些输出:

STOCK CLOSE_DATE PTTRN OPEN HIGH LOW CLOSE VOLUME
XYZ 01/01/2021 INITIAL_VALUE 40 40.5 38.5 38.8 83057
XYZ 02/01/2021 DOWN 39.2 39.2 37.2 37.8 181814
XYZ 03/01/2021 DOWN 38 38.5 36.5 37 117378
XYZ 04/01/2021 DOWN 36.5 36.6 35.6 35.7 93737
XYZ 05/01/2021 BULLISH_ENGULFING 35.35 36.8 35.1 36.7 169106
XYZ 06/01/2021 UP 36.5 38.5 36.5 38 123179
XYZ 07/01/2021 UP 37.5 39.5 37.3 39.4 282986
XYZ 08/01/2021 UP 39 40.5 38.5 40 117437
XYZ 09/01/2021 BEARISH_HARAMI 39.7 39.8 39.3 39.4 873009
XYZ 10/01/2021 DOWN 39.2 39.2 37.2 37.8 62522
XYZ 11/01/2021 DOWN 38 38.5 36.5 37 114826
XYZ 12/01/2021 PIERCING_LINE 36.5 37.9 36.3 37.8 281461
XYZ 13/01/2021 UP 37.5 39.5 37.3 39.4 77334
XYZ 14/01/2021 UP 39 40.5 38.5 40 321684

db小提琴here

【讨论】:

非常感谢@MT0。如果我在 SQL 命令中运行此查询但在 Oracle Apex 的前端出现问题,它工作正常。给出错误“ORA-00911:无效字符”。我认为这是因为克拉和美元符号。我可以知道是否有解决此问题的方法吗?提前致谢。 嗨@MT0,现在我知道如何添加最近一天的收盘价和前一天的收盘价。我可以知道如何添加以下公式: 4 天前 CLOSE 不到 4 天前 OPEN 和 3 天前 CLOSE 不到 3 天前 OPEN 和 2 天前 CLOSE 不到 2 天前 OPEN close &lt; LAG(open, 4) OVER (PARTITION BY stock ORDER BY close_date) 用于第一个,我相信您可以将其调整为第二个和第三个...如果您的模式为 4-,您可能可以使用 MATCH_RECOGNIZE天宽,但没有足够的背景说明您将如何匹配中间的天数才能给出答案。 @Richa 你的代码可以工作db<>fiddle。 哇,你真是天才@MT0。你拯救了我的一天。再次感谢您的快速帮助。我非常感谢。你让它听起来很简单。我将案例语句放在定义块和模式中。我的错。非常感谢。【参考方案2】:

我赞成@MT0 的回答,我自己会使用match_recognize 来解决这类问题,因为这正是它旨在处理的问题。但是,match_recognize 是一个非常复杂的构造,您正在寻找的模式非常简单。如前所述,您可以通过只使用几个lag 分析函数的更简单的查询来解决您的问题。随着您正在寻找的模式变得越来越复杂,您会发现使用match_recognize 来表达它们会更容易,而仅使用lag 来处理它们会更困难,但是当前的问题相对容易以这种方式表达.

请注意,我正在对 @MT0 所做的“穿刺线”公式进行相同的更改

with data as (
select src.stock,
       src.close_date,
       src.open o,
       src.close c,
       src.high h,
       src.low l,
       lag(src.open) over (partition by src.stock order by src.close_date) o1,
       lag(src.close) over (partition by src.stock order by src.close_date) c1,
       lag(src.high) over (partition by src.stock order by src.close_date) h1,
       lag(src.low) over (partition by src.stock order by src.close_date) l1
  from raw_source src
)
select d.*,
       case when o1 > c1 and c > o and c > h1 and o < l1
            then 'Bullish Engulfing'
            when (O1 < C1) and (O > C) and (O < C1) and (C > O1) and (H < H1) and (L > L1)
            then 'Bearish Harami'
            when (O < C) and (O1 > C1) and (C > (C1 + O1)/2) and (O < C1) and (C < O1)
            then 'Piercing Line'
         end pattern
  from data d
     

在this dbfiddle 的pattern 列中产生相同的结果。不过,由于我们可以使用您用来表达公式的相同语法,因此遵循此查询中的逻辑可能比理解 match_recognize 语法更容易。

【讨论】:

非常感谢@JustinCave。我非常感谢您的时间和帮助。也感谢您在 OP 中的更正。我想知道是否有办法计算出股票下跌或上涨了多少天。例如,根据 OP 中的屏幕截图,如果今天的日期是 2021 年 5 月 1 日(DD/MM/YYYY),是否可以可视化或获得输出表明过去 4 天趋势下降? @Richa - 你如何定义“向下”或“向上”?当前收盘价低于前一天收盘价?还是别的什么? 嗨@JustinCave。这是一个非常好的问题:通过使用下面的查询,我得到“连续天数”,它根据收盘价显示趋势是否处于下行趋势。 WITH StockRow AS (SELECT stock, close, close_date, ROW_NUMBER() OVER(PARTITION BY stock ORDER BY close_date) rn FROM raw_source), RunGroup AS (SELECT Base.stock, Base.close_date, MAX(Restart.rn) OVER(PARTITION BY Base.stock ORDER BY Base.close_date) groupingId FROM StockRow Base LEFT JOIN StockRow Restart ON Restart.stock = Base.stock AND Restart.rn = Base.rn - 1 AND Restart.close > Base.close).. ..SELECT stock, COUNT() AS continuousCount, MIN(close_date) AS startDate, MAX(close_date) AS endDate FROM RunGroup GROUP BY stock, groupingId HAVING COUNT() >= 3 按库存订购,开始日期 因为有字数限制。将查询发布在两个不同的 cmets 中。这就是我想要实现的目标,即首先要识别趋势。第二,结合趋势识别烛台形态。根据 OP 中的屏幕截图

以上是关于Oracle SQL 或 PL/SQL:如何仅在上升趋势或下降趋势结束时识别烛台形态并在列中设置标志?的主要内容,如果未能解决你的问题,请参考以下文章

我们如何在 Oracle SQL 或 PL/SQL 中实现 Standard Normal CDF?

Oracle - 两个表中两行之间的差异或变化

Oracle:使用 SQL 或 PL/SQL 查找动态 SQL 中的错误位置

如何在一组行之后或有条件地在没有 PL/SQL 块的情况下增加 oracle 序列?

oracle PL/SQL编程语言之游标的使用

oracle PL/SQL编程语言之游标的使用