雅虎财经的比例错误 python

wrong scale in yahoo finance python

我正在尝试使用 yahoo finance 下载一些共同基金和 ETF 的历史价格。我认为 Yahoo Finance 中有一个错误混淆了价格的比例(我不认为这是一个分裂问题。见下图)。

无论如何这里有一个重现问题的 MWE

import yfinance as yf
import pandas as pd 
from pandas_datareader import data as pdr
yf.pdr_override()

tickers = ["0P0001FE43.L", "0P00014IJX.L", "SGLN.L"]

start_date = "2019-01-01" 
today      = "2021-04-27"

def getData(ticker):
        #print (ticker)
        data = pdr.get_data_yahoo(ticker, start=start_date, end=today)
        data["yahoo_ticker"] = ticker
        files.append(data)

files=[]
for tik in tickers:
    getData(tik)

df = pd.concat(files)
df = df[ [ "Adj Close", "yahoo_ticker"]]

仔细查看调整后的价格会发现:

我想不出任何系统的方法来纠正这个问题,因此非常感谢您的帮助。

我很快查看了SGLN.L只有yahoo的财经页面和数据对齐。我不是市场专家,所以我不能说这里发生了什么,但数据似乎是匹配的。此外,交易量 up/down 在此期间变得更加不稳定,因此可能与此有关。

要捕获并修复 一天 事件 我通常会检查非常大的 一天 差距。像这样的东西,将每次收盘价与前一次和下一次进行比较,应该可以解决 1/100 或 1*100 的问题,但没有什么能阻止您设置另一个阈值或一次使用多个阈值。当历史序列中的差距是由于管理不善的拆分导致时,多重比较可以避免更正。

代码:

fixed = [l[0]]

i = 1

while i < len(l) -1:
        
    p = l[i] / l[i-1]
    n = l[i+1] / l[i]

    if p < 0.1 and n >= 100:
        fixed.append(l[i] * 100)      
    elif p >= 100 and n < 0.1:
        fixed.append(round(l[i] / 100, 5))
    else:
        fixed.append(l[i])
    
    i += 1
            
# Last value
        
p =  fixed[-1] / l[-1]

if p >= 100:
    fixed.append(l[i] * 100)      
elif p < 0.1:
    fixed.append(round(l[i] / 100, 5))
else:
    fixed.append(l[i])

输出:

l = [171.062, 1.71945, 172.901, 172.184]
[171.062, 171.945, 172.901, 172.184]

l = [1.71062, 1.71945, 172.901, 1.72184]
[1.71062, 1.71945, 1.72901, 1.72184]

虽然这可行,但如果您可以下载所有(调整后的)OHLC 字段以获得安全性(我使用 Bloomberg Professional 作为数据提供者,我不知道 Yahoo 是否支持它)您应该始终将它们相互比较而不是仅将收盘价与前一个和下一个进行比较。这将是一种更稳健的方法,同时您还可以检查 O 和 C 是否在 H - L 范围内以及其他更常见但更细微的错误。

一种方法是简单地发现几乎 100 倍的差异,遗憾的是股票可能会波动很大,所以这可能并不总是有效:

import yfinance as yf
import pandas as pd
from pandas_datareader import data as pdr
yf.pdr_override()

tickers = ["0P0001FE43.L", "0P00014IJX.L", "SGLN.L"]

start_date = "2019-01-01"
today      = "2021-04-27"

def getData(ticker):
    data = pdr.get_data_yahoo(ticker, start=start_date, end=today)
    data["yahoo_ticker"] = ticker
    # fix data where it's more than 40 times the min
    mask = data['Adj Close'] < 40 * (data['Adj Close'].min())
    data['Adj Close'] = data['Adj Close'] * (1 + 99 * mask)
    print(f'Fixed {sum(mask)}/{len(mask)} datapoints for ticker {ticker}')
    return data

df = pd.concat([getData(tik) for tik in tickers])
df = df[["Adj Close", "yahoo_ticker"]]
print(df)
Fixed 5/587 datapoints for ticker 0P0001FE43.L
Fixed 1/587 datapoints for ticker 0P00014IJX.L
Fixed 288/586 datapoints for ticker SGLN.L

我找到了另一个我认为应该比我的其他答案更可靠的解决方案。这并没有考虑整个历史,而是针对 50 倍或更多的单日跳跃,这永远不会“自然”发生。

import yfinance as yf
import pandas as pd
from pandas_datareader import data as pdr
yf.pdr_override()

tickers = ["0P0001FE43.L", "0P00014IJX.L", "SGLN.L"]

start_date = "2019-01-01"
today      = "2021-04-27"

def getData(ticker):
    data = pdr.get_data_yahoo(ticker, start=start_date, end=today)
    data["yahoo_ticker"] = ticker
    # fix faulty yahoo data that jumps 100x
    jumps_up   = data['Adj Close'] / data['Adj Close'].shift() >  50
    jumps_down = data['Adj Close'] / data['Adj Close'].shift() < .02
    correction_factor = 100.**(jumps_down.cumsum() - jumps_up.cumsum())
    data['Adj Close'] *= correction_factor
    print(f"Fixed {sum(correction_factor != 1)}/{len(data)} for ticker {ticker}"
          f" (min: {data['Adj Close'].min()}, max: {data['Adj Close'].max()})")
    return data

df = pd.concat([getData(tik) for tik in tickers])
df = df[["Adj Close", "yahoo_ticker"]]
print(df)
Fixed 5/587 for ticker 0P0001FE43.L (min: 97.52300262451172, max: 174.1580047607422)
Fixed 1/587 for ticker 0P00014IJX.L (min: 169.8939971923828, max: 376.5379943847656)
Fixed 288/586 for ticker SGLN.L (min: 2195.0, max: 3388.9999389648438)

根据 Yahoo Finance 这实际上是数据集中的错误,而不是程序包中的错误。

对于几天受影响的情况,我建议用 NaN 替换错误行,然后使用 pandas.DataFrame.interpolate()

faulty = df['Adj Close'].le(df['Adj Close'].shift()*0.1)
df.at[faulty[faulty].index, :] = np.nan
df['Adj Close'] = df['Adj Close'].interpolate()