Merge pull request #1638 from ricardoprins/dev

PEP 8 changes + minor performance improvements
pull/1640/head
ValueRaider 2023-07-26 17:49:25 +01:00 committed by GitHub
commit 9908c1ff48
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
9 changed files with 273 additions and 174 deletions

View File

@ -39,7 +39,7 @@ setup(
'License :: OSI Approved :: Apache Software License',
# 'Development Status :: 3 - Alpha',
'Development Status :: 4 - Beta',
#'Development Status :: 5 - Production/Stable',
# 'Development Status :: 5 - Production/Stable',
'Operating System :: OS Independent',

View File

@ -1,8 +1,115 @@
fundamentals_keys = {}
fundamentals_keys = {
'financials': ["TaxEffectOfUnusualItems", "TaxRateForCalcs", "NormalizedEBITDA", "NormalizedDilutedEPS",
"NormalizedBasicEPS", "TotalUnusualItems", "TotalUnusualItemsExcludingGoodwill",
"NetIncomeFromContinuingOperationNetMinorityInterest", "ReconciledDepreciation",
"ReconciledCostOfRevenue", "EBITDA", "EBIT", "NetInterestIncome", "InterestExpense",
"InterestIncome", "ContinuingAndDiscontinuedDilutedEPS", "ContinuingAndDiscontinuedBasicEPS",
"NormalizedIncome", "NetIncomeFromContinuingAndDiscontinuedOperation", "TotalExpenses",
"RentExpenseSupplemental", "ReportedNormalizedDilutedEPS", "ReportedNormalizedBasicEPS",
"TotalOperatingIncomeAsReported", "DividendPerShare", "DilutedAverageShares", "BasicAverageShares",
"DilutedEPS", "DilutedEPSOtherGainsLosses", "TaxLossCarryforwardDilutedEPS",
"DilutedAccountingChange", "DilutedExtraordinary", "DilutedDiscontinuousOperations",
"DilutedContinuousOperations", "BasicEPS", "BasicEPSOtherGainsLosses", "TaxLossCarryforwardBasicEPS",
"BasicAccountingChange", "BasicExtraordinary", "BasicDiscontinuousOperations",
"BasicContinuousOperations", "DilutedNIAvailtoComStockholders", "AverageDilutionEarnings",
"NetIncomeCommonStockholders", "OtherunderPreferredStockDividend", "PreferredStockDividends",
"NetIncome", "MinorityInterests", "NetIncomeIncludingNoncontrollingInterests",
"NetIncomeFromTaxLossCarryforward", "NetIncomeExtraordinary", "NetIncomeDiscontinuousOperations",
"NetIncomeContinuousOperations", "EarningsFromEquityInterestNetOfTax", "TaxProvision",
"PretaxIncome", "OtherIncomeExpense", "OtherNonOperatingIncomeExpenses", "SpecialIncomeCharges",
"GainOnSaleOfPPE", "GainOnSaleOfBusiness", "OtherSpecialCharges", "WriteOff",
"ImpairmentOfCapitalAssets", "RestructuringAndMergernAcquisition", "SecuritiesAmortization",
"EarningsFromEquityInterest", "GainOnSaleOfSecurity", "NetNonOperatingInterestIncomeExpense",
"TotalOtherFinanceCost", "InterestExpenseNonOperating", "InterestIncomeNonOperating",
"OperatingIncome", "OperatingExpense", "OtherOperatingExpenses", "OtherTaxes",
"ProvisionForDoubtfulAccounts", "DepreciationAmortizationDepletionIncomeStatement",
"DepletionIncomeStatement", "DepreciationAndAmortizationInIncomeStatement", "Amortization",
"AmortizationOfIntangiblesIncomeStatement", "DepreciationIncomeStatement", "ResearchAndDevelopment",
"SellingGeneralAndAdministration", "SellingAndMarketingExpense", "GeneralAndAdministrativeExpense",
"OtherGandA", "InsuranceAndClaims", "RentAndLandingFees", "SalariesAndWages", "GrossProfit",
"CostOfRevenue", "TotalRevenue", "ExciseTaxes", "OperatingRevenue"],
'balance-sheet': ["TreasurySharesNumber", "PreferredSharesNumber", "OrdinarySharesNumber", "ShareIssued", "NetDebt",
"TotalDebt", "TangibleBookValue", "InvestedCapital", "WorkingCapital", "NetTangibleAssets",
"CapitalLeaseObligations", "CommonStockEquity", "PreferredStockEquity", "TotalCapitalization",
"TotalEquityGrossMinorityInterest", "MinorityInterest", "StockholdersEquity",
"OtherEquityInterest", "GainsLossesNotAffectingRetainedEarnings", "OtherEquityAdjustments",
"FixedAssetsRevaluationReserve", "ForeignCurrencyTranslationAdjustments",
"MinimumPensionLiabilities", "UnrealizedGainLoss", "TreasuryStock", "RetainedEarnings",
"AdditionalPaidInCapital", "CapitalStock", "OtherCapitalStock", "CommonStock", "PreferredStock",
"TotalPartnershipCapital", "GeneralPartnershipCapital", "LimitedPartnershipCapital",
"TotalLiabilitiesNetMinorityInterest", "TotalNonCurrentLiabilitiesNetMinorityInterest",
"OtherNonCurrentLiabilities", "LiabilitiesHeldforSaleNonCurrent", "RestrictedCommonStock",
"PreferredSecuritiesOutsideStockEquity", "DerivativeProductLiabilities", "EmployeeBenefits",
"NonCurrentPensionAndOtherPostretirementBenefitPlans", "NonCurrentAccruedExpenses",
"DuetoRelatedPartiesNonCurrent", "TradeandOtherPayablesNonCurrent",
"NonCurrentDeferredLiabilities", "NonCurrentDeferredRevenue",
"NonCurrentDeferredTaxesLiabilities", "LongTermDebtAndCapitalLeaseObligation",
"LongTermCapitalLeaseObligation", "LongTermDebt", "LongTermProvisions", "CurrentLiabilities",
"OtherCurrentLiabilities", "CurrentDeferredLiabilities", "CurrentDeferredRevenue",
"CurrentDeferredTaxesLiabilities", "CurrentDebtAndCapitalLeaseObligation",
"CurrentCapitalLeaseObligation", "CurrentDebt", "OtherCurrentBorrowings", "LineOfCredit",
"CommercialPaper", "CurrentNotesPayable", "PensionandOtherPostRetirementBenefitPlansCurrent",
"CurrentProvisions", "PayablesAndAccruedExpenses", "CurrentAccruedExpenses", "InterestPayable",
"Payables", "OtherPayable", "DuetoRelatedPartiesCurrent", "DividendsPayable", "TotalTaxPayable",
"IncomeTaxPayable", "AccountsPayable", "TotalAssets", "TotalNonCurrentAssets",
"OtherNonCurrentAssets", "DefinedPensionBenefit", "NonCurrentPrepaidAssets",
"NonCurrentDeferredAssets", "NonCurrentDeferredTaxesAssets", "DuefromRelatedPartiesNonCurrent",
"NonCurrentNoteReceivables", "NonCurrentAccountsReceivable", "FinancialAssets",
"InvestmentsAndAdvances", "OtherInvestments", "InvestmentinFinancialAssets",
"HeldToMaturitySecurities", "AvailableForSaleSecurities",
"FinancialAssetsDesignatedasFairValueThroughProfitorLossTotal", "TradingSecurities",
"LongTermEquityInvestment", "InvestmentsinJointVenturesatCost",
"InvestmentsInOtherVenturesUnderEquityMethod", "InvestmentsinAssociatesatCost",
"InvestmentsinSubsidiariesatCost", "InvestmentProperties", "GoodwillAndOtherIntangibleAssets",
"OtherIntangibleAssets", "Goodwill", "NetPPE", "AccumulatedDepreciation", "GrossPPE", "Leases",
"ConstructionInProgress", "OtherProperties", "MachineryFurnitureEquipment",
"BuildingsAndImprovements", "LandAndImprovements", "Properties", "CurrentAssets",
"OtherCurrentAssets", "HedgingAssetsCurrent", "AssetsHeldForSaleCurrent", "CurrentDeferredAssets",
"CurrentDeferredTaxesAssets", "RestrictedCash", "PrepaidAssets", "Inventory",
"InventoriesAdjustmentsAllowances", "OtherInventories", "FinishedGoods", "WorkInProcess",
"RawMaterials", "Receivables", "ReceivablesAdjustmentsAllowances", "OtherReceivables",
"DuefromRelatedPartiesCurrent", "TaxesReceivable", "AccruedInterestReceivable", "NotesReceivable",
"LoansReceivable", "AccountsReceivable", "AllowanceForDoubtfulAccountsReceivable",
"GrossAccountsReceivable", "CashCashEquivalentsAndShortTermInvestments",
"OtherShortTermInvestments", "CashAndCashEquivalents", "CashEquivalents", "CashFinancial"],
'cash-flow': ["ForeignSales", "DomesticSales", "AdjustedGeographySegmentData", "FreeCashFlow",
"RepurchaseOfCapitalStock", "RepaymentOfDebt", "IssuanceOfDebt", "IssuanceOfCapitalStock",
"CapitalExpenditure", "InterestPaidSupplementalData", "IncomeTaxPaidSupplementalData",
"EndCashPosition", "OtherCashAdjustmentOutsideChangeinCash", "BeginningCashPosition",
"EffectOfExchangeRateChanges", "ChangesInCash", "OtherCashAdjustmentInsideChangeinCash",
"CashFlowFromDiscontinuedOperation", "FinancingCashFlow", "CashFromDiscontinuedFinancingActivities",
"CashFlowFromContinuingFinancingActivities", "NetOtherFinancingCharges", "InterestPaidCFF",
"ProceedsFromStockOptionExercised", "CashDividendsPaid", "PreferredStockDividendPaid",
"CommonStockDividendPaid", "NetPreferredStockIssuance", "PreferredStockPayments",
"PreferredStockIssuance", "NetCommonStockIssuance", "CommonStockPayments", "CommonStockIssuance",
"NetIssuancePaymentsOfDebt", "NetShortTermDebtIssuance", "ShortTermDebtPayments",
"ShortTermDebtIssuance", "NetLongTermDebtIssuance", "LongTermDebtPayments", "LongTermDebtIssuance",
"InvestingCashFlow", "CashFromDiscontinuedInvestingActivities",
"CashFlowFromContinuingInvestingActivities", "NetOtherInvestingChanges", "InterestReceivedCFI",
"DividendsReceivedCFI", "NetInvestmentPurchaseAndSale", "SaleOfInvestment", "PurchaseOfInvestment",
"NetInvestmentPropertiesPurchaseAndSale", "SaleOfInvestmentProperties",
"PurchaseOfInvestmentProperties", "NetBusinessPurchaseAndSale", "SaleOfBusiness",
"PurchaseOfBusiness", "NetIntangiblesPurchaseAndSale", "SaleOfIntangibles", "PurchaseOfIntangibles",
"NetPPEPurchaseAndSale", "SaleOfPPE", "PurchaseOfPPE", "CapitalExpenditureReported",
"OperatingCashFlow", "CashFromDiscontinuedOperatingActivities",
"CashFlowFromContinuingOperatingActivities", "TaxesRefundPaid", "InterestReceivedCFO",
"InterestPaidCFO", "DividendReceivedCFO", "DividendPaidCFO", "ChangeInWorkingCapital",
"ChangeInOtherWorkingCapital", "ChangeInOtherCurrentLiabilities", "ChangeInOtherCurrentAssets",
"ChangeInPayablesAndAccruedExpense", "ChangeInAccruedExpense", "ChangeInInterestPayable",
"ChangeInPayable", "ChangeInDividendPayable", "ChangeInAccountPayable", "ChangeInTaxPayable",
"ChangeInIncomeTaxPayable", "ChangeInPrepaidAssets", "ChangeInInventory", "ChangeInReceivables",
"ChangesInAccountReceivables", "OtherNonCashItems", "ExcessTaxBenefitFromStockBasedCompensation",
"StockBasedCompensation", "UnrealizedGainLossOnInvestmentSecurities", "ProvisionandWriteOffofAssets",
"AssetImpairmentCharge", "AmortizationOfSecurities", "DeferredTax", "DeferredIncomeTax",
"DepreciationAmortizationDepletion", "Depletion", "DepreciationAndAmortization",
"AmortizationCashFlow", "AmortizationOfIntangibles", "Depreciation", "OperatingGainsLosses",
"PensionAndEmployeeBenefitExpense", "EarningsLossesFromEquityInvestments",
"GainLossOnInvestmentSecurities", "NetForeignCurrencyExchangeGainLoss", "GainLossOnSaleOfPPE",
"GainLossOnSaleOfBusiness", "NetIncomeFromContinuingOperations",
"CashFlowsfromusedinOperatingActivitiesDirect", "TaxesRefundPaidDirect", "InterestReceivedDirect",
"InterestPaidDirect", "DividendsReceivedDirect", "DividendsPaidDirect", "ClassesofCashPayments",
"OtherCashPaymentsfromOperatingActivities", "PaymentsonBehalfofEmployees",
"PaymentstoSuppliersforGoodsandServices", "ClassesofCashReceiptsfromOperatingActivities",
"OtherCashReceiptsfromOperatingActivities", "ReceiptsfromGovernmentGrants", "ReceiptsfromCustomers"]}
fundamentals_keys['financials'] = ["TaxEffectOfUnusualItems","TaxRateForCalcs","NormalizedEBITDA","NormalizedDilutedEPS","NormalizedBasicEPS","TotalUnusualItems","TotalUnusualItemsExcludingGoodwill","NetIncomeFromContinuingOperationNetMinorityInterest","ReconciledDepreciation","ReconciledCostOfRevenue","EBITDA","EBIT","NetInterestIncome","InterestExpense","InterestIncome","ContinuingAndDiscontinuedDilutedEPS","ContinuingAndDiscontinuedBasicEPS","NormalizedIncome","NetIncomeFromContinuingAndDiscontinuedOperation","TotalExpenses","RentExpenseSupplemental","ReportedNormalizedDilutedEPS","ReportedNormalizedBasicEPS","TotalOperatingIncomeAsReported","DividendPerShare","DilutedAverageShares","BasicAverageShares","DilutedEPS","DilutedEPSOtherGainsLosses","TaxLossCarryforwardDilutedEPS","DilutedAccountingChange","DilutedExtraordinary","DilutedDiscontinuousOperations","DilutedContinuousOperations","BasicEPS","BasicEPSOtherGainsLosses","TaxLossCarryforwardBasicEPS","BasicAccountingChange","BasicExtraordinary","BasicDiscontinuousOperations","BasicContinuousOperations","DilutedNIAvailtoComStockholders","AverageDilutionEarnings","NetIncomeCommonStockholders","OtherunderPreferredStockDividend","PreferredStockDividends","NetIncome","MinorityInterests","NetIncomeIncludingNoncontrollingInterests","NetIncomeFromTaxLossCarryforward","NetIncomeExtraordinary","NetIncomeDiscontinuousOperations","NetIncomeContinuousOperations","EarningsFromEquityInterestNetOfTax","TaxProvision","PretaxIncome","OtherIncomeExpense","OtherNonOperatingIncomeExpenses","SpecialIncomeCharges","GainOnSaleOfPPE","GainOnSaleOfBusiness","OtherSpecialCharges","WriteOff","ImpairmentOfCapitalAssets","RestructuringAndMergernAcquisition","SecuritiesAmortization","EarningsFromEquityInterest","GainOnSaleOfSecurity","NetNonOperatingInterestIncomeExpense","TotalOtherFinanceCost","InterestExpenseNonOperating","InterestIncomeNonOperating","OperatingIncome","OperatingExpense","OtherOperatingExpenses","OtherTaxes","ProvisionForDoubtfulAccounts","DepreciationAmortizationDepletionIncomeStatement","DepletionIncomeStatement","DepreciationAndAmortizationInIncomeStatement","Amortization","AmortizationOfIntangiblesIncomeStatement","DepreciationIncomeStatement","ResearchAndDevelopment","SellingGeneralAndAdministration","SellingAndMarketingExpense","GeneralAndAdministrativeExpense","OtherGandA","InsuranceAndClaims","RentAndLandingFees","SalariesAndWages","GrossProfit","CostOfRevenue","TotalRevenue","ExciseTaxes","OperatingRevenue"]
fundamentals_keys['balance-sheet'] = ["TreasurySharesNumber","PreferredSharesNumber","OrdinarySharesNumber","ShareIssued","NetDebt","TotalDebt","TangibleBookValue","InvestedCapital","WorkingCapital","NetTangibleAssets","CapitalLeaseObligations","CommonStockEquity","PreferredStockEquity","TotalCapitalization","TotalEquityGrossMinorityInterest","MinorityInterest","StockholdersEquity","OtherEquityInterest","GainsLossesNotAffectingRetainedEarnings","OtherEquityAdjustments","FixedAssetsRevaluationReserve","ForeignCurrencyTranslationAdjustments","MinimumPensionLiabilities","UnrealizedGainLoss","TreasuryStock","RetainedEarnings","AdditionalPaidInCapital","CapitalStock","OtherCapitalStock","CommonStock","PreferredStock","TotalPartnershipCapital","GeneralPartnershipCapital","LimitedPartnershipCapital","TotalLiabilitiesNetMinorityInterest","TotalNonCurrentLiabilitiesNetMinorityInterest","OtherNonCurrentLiabilities","LiabilitiesHeldforSaleNonCurrent","RestrictedCommonStock","PreferredSecuritiesOutsideStockEquity","DerivativeProductLiabilities","EmployeeBenefits","NonCurrentPensionAndOtherPostretirementBenefitPlans","NonCurrentAccruedExpenses","DuetoRelatedPartiesNonCurrent","TradeandOtherPayablesNonCurrent","NonCurrentDeferredLiabilities","NonCurrentDeferredRevenue","NonCurrentDeferredTaxesLiabilities","LongTermDebtAndCapitalLeaseObligation","LongTermCapitalLeaseObligation","LongTermDebt","LongTermProvisions","CurrentLiabilities","OtherCurrentLiabilities","CurrentDeferredLiabilities","CurrentDeferredRevenue","CurrentDeferredTaxesLiabilities","CurrentDebtAndCapitalLeaseObligation","CurrentCapitalLeaseObligation","CurrentDebt","OtherCurrentBorrowings","LineOfCredit","CommercialPaper","CurrentNotesPayable","PensionandOtherPostRetirementBenefitPlansCurrent","CurrentProvisions","PayablesAndAccruedExpenses","CurrentAccruedExpenses","InterestPayable","Payables","OtherPayable","DuetoRelatedPartiesCurrent","DividendsPayable","TotalTaxPayable","IncomeTaxPayable","AccountsPayable","TotalAssets","TotalNonCurrentAssets","OtherNonCurrentAssets","DefinedPensionBenefit","NonCurrentPrepaidAssets","NonCurrentDeferredAssets","NonCurrentDeferredTaxesAssets","DuefromRelatedPartiesNonCurrent","NonCurrentNoteReceivables","NonCurrentAccountsReceivable","FinancialAssets","InvestmentsAndAdvances","OtherInvestments","InvestmentinFinancialAssets","HeldToMaturitySecurities","AvailableForSaleSecurities","FinancialAssetsDesignatedasFairValueThroughProfitorLossTotal","TradingSecurities","LongTermEquityInvestment","InvestmentsinJointVenturesatCost","InvestmentsInOtherVenturesUnderEquityMethod","InvestmentsinAssociatesatCost","InvestmentsinSubsidiariesatCost","InvestmentProperties","GoodwillAndOtherIntangibleAssets","OtherIntangibleAssets","Goodwill","NetPPE","AccumulatedDepreciation","GrossPPE","Leases","ConstructionInProgress","OtherProperties","MachineryFurnitureEquipment","BuildingsAndImprovements","LandAndImprovements","Properties","CurrentAssets","OtherCurrentAssets","HedgingAssetsCurrent","AssetsHeldForSaleCurrent","CurrentDeferredAssets","CurrentDeferredTaxesAssets","RestrictedCash","PrepaidAssets","Inventory","InventoriesAdjustmentsAllowances","OtherInventories","FinishedGoods","WorkInProcess","RawMaterials","Receivables","ReceivablesAdjustmentsAllowances","OtherReceivables","DuefromRelatedPartiesCurrent","TaxesReceivable","AccruedInterestReceivable","NotesReceivable","LoansReceivable","AccountsReceivable","AllowanceForDoubtfulAccountsReceivable","GrossAccountsReceivable","CashCashEquivalentsAndShortTermInvestments","OtherShortTermInvestments","CashAndCashEquivalents","CashEquivalents","CashFinancial"]
fundamentals_keys['cash-flow'] = ["ForeignSales","DomesticSales","AdjustedGeographySegmentData","FreeCashFlow","RepurchaseOfCapitalStock","RepaymentOfDebt","IssuanceOfDebt","IssuanceOfCapitalStock","CapitalExpenditure","InterestPaidSupplementalData","IncomeTaxPaidSupplementalData","EndCashPosition","OtherCashAdjustmentOutsideChangeinCash","BeginningCashPosition","EffectOfExchangeRateChanges","ChangesInCash","OtherCashAdjustmentInsideChangeinCash","CashFlowFromDiscontinuedOperation","FinancingCashFlow","CashFromDiscontinuedFinancingActivities","CashFlowFromContinuingFinancingActivities","NetOtherFinancingCharges","InterestPaidCFF","ProceedsFromStockOptionExercised","CashDividendsPaid","PreferredStockDividendPaid","CommonStockDividendPaid","NetPreferredStockIssuance","PreferredStockPayments","PreferredStockIssuance","NetCommonStockIssuance","CommonStockPayments","CommonStockIssuance","NetIssuancePaymentsOfDebt","NetShortTermDebtIssuance","ShortTermDebtPayments","ShortTermDebtIssuance","NetLongTermDebtIssuance","LongTermDebtPayments","LongTermDebtIssuance","InvestingCashFlow","CashFromDiscontinuedInvestingActivities","CashFlowFromContinuingInvestingActivities","NetOtherInvestingChanges","InterestReceivedCFI","DividendsReceivedCFI","NetInvestmentPurchaseAndSale","SaleOfInvestment","PurchaseOfInvestment","NetInvestmentPropertiesPurchaseAndSale","SaleOfInvestmentProperties","PurchaseOfInvestmentProperties","NetBusinessPurchaseAndSale","SaleOfBusiness","PurchaseOfBusiness","NetIntangiblesPurchaseAndSale","SaleOfIntangibles","PurchaseOfIntangibles","NetPPEPurchaseAndSale","SaleOfPPE","PurchaseOfPPE","CapitalExpenditureReported","OperatingCashFlow","CashFromDiscontinuedOperatingActivities","CashFlowFromContinuingOperatingActivities","TaxesRefundPaid","InterestReceivedCFO","InterestPaidCFO","DividendReceivedCFO","DividendPaidCFO","ChangeInWorkingCapital","ChangeInOtherWorkingCapital","ChangeInOtherCurrentLiabilities","ChangeInOtherCurrentAssets","ChangeInPayablesAndAccruedExpense","ChangeInAccruedExpense","ChangeInInterestPayable","ChangeInPayable","ChangeInDividendPayable","ChangeInAccountPayable","ChangeInTaxPayable","ChangeInIncomeTaxPayable","ChangeInPrepaidAssets","ChangeInInventory","ChangeInReceivables","ChangesInAccountReceivables","OtherNonCashItems","ExcessTaxBenefitFromStockBasedCompensation","StockBasedCompensation","UnrealizedGainLossOnInvestmentSecurities","ProvisionandWriteOffofAssets","AssetImpairmentCharge","AmortizationOfSecurities","DeferredTax","DeferredIncomeTax","DepreciationAmortizationDepletion","Depletion","DepreciationAndAmortization","AmortizationCashFlow","AmortizationOfIntangibles","Depreciation","OperatingGainsLosses","PensionAndEmployeeBenefitExpense","EarningsLossesFromEquityInvestments","GainLossOnInvestmentSecurities","NetForeignCurrencyExchangeGainLoss","GainLossOnSaleOfPPE","GainLossOnSaleOfBusiness","NetIncomeFromContinuingOperations","CashFlowsfromusedinOperatingActivitiesDirect","TaxesRefundPaidDirect","InterestReceivedDirect","InterestPaidDirect","DividendsReceivedDirect","DividendsPaidDirect","ClassesofCashPayments","OtherCashPaymentsfromOperatingActivities","PaymentsonBehalfofEmployees","PaymentstoSuppliersforGoodsandServices","ClassesofCashReceiptsfromOperatingActivities","OtherCashReceiptsfromOperatingActivities","ReceiptsfromGovernmentGrants","ReceiptsfromCustomers"]

View File

@ -22,14 +22,16 @@
from __future__ import print_function
import logging
import traceback
import time as _time
import traceback
import multitasking as _multitasking
import pandas as _pd
from . import Ticker, utils
from . import shared
@utils.log_indent_decorator
def download(tickers, start=None, end=None, actions=False, threads=True, ignore_tz=None,
group_by='column', auto_adjust=False, back_adjust=False, repair=False, keepna=False,
@ -181,7 +183,7 @@ def download(tickers, start=None, end=None, actions=False, threads=True, ignore_
for ticker in shared._ERRORS:
err = shared._ERRORS[ticker]
err = err.replace(f'{ticker}', '%ticker%')
if not err in errors:
if err not in errors:
errors[err] = [ticker]
else:
errors[err].append(ticker)
@ -193,7 +195,7 @@ def download(tickers, start=None, end=None, actions=False, threads=True, ignore_
for ticker in shared._TRACEBACKS:
tb = shared._TRACEBACKS[ticker]
tb = tb.replace(f'{ticker}', '%ticker%')
if not tb in tbs:
if tb not in tbs:
tbs[tb] = [ticker]
else:
tbs[tb].append(ticker)

View File

@ -1,14 +1,13 @@
import datetime
import logging
import json
import pandas as pd
import numpy as np
from yfinance import utils, const
from yfinance.data import TickerData
from yfinance.exceptions import YFinanceException, YFNotImplementedError
class Fundamentals:
def __init__(self, data: TickerData, proxy=None):
@ -76,9 +75,9 @@ class Financials:
allowed_timescales = ["yearly", "quarterly"]
if name not in allowed_names:
raise ValueError("Illegal argument: name must be one of: {}".format(allowed_names))
raise ValueError(f"Illegal argument: name must be one of: {allowed_names}")
if timescale not in allowed_timescales:
raise ValueError("Illegal argument: timescale must be one of: {}".format(allowed_names))
raise ValueError(f"Illegal argument: timescale must be one of: {allowed_names}")
try:
statement = self._create_financials_table(name, timescale, proxy)
@ -86,7 +85,7 @@ class Financials:
if statement is not None:
return statement
except YFinanceException as e:
utils.get_yf_logger().error("%s: Failed to create %s financials table for reason: %r", self._data.ticker, name, e)
utils.get_yf_logger().error(f"{self._data.ticker}: Failed to create {name} financials table for reason: {e}")
return pd.DataFrame()
def _create_financials_table(self, name, timescale, proxy):
@ -106,15 +105,12 @@ class Financials:
timescale = timescale_translation[timescale]
# Step 2: construct url:
ts_url_base = \
"https://query2.finance.yahoo.com/ws/fundamentals-timeseries/v1/finance/timeseries/{0}?symbol={0}" \
.format(self._data.ticker)
ts_url_base = f"https://query2.finance.yahoo.com/ws/fundamentals-timeseries/v1/finance/timeseries/{self._data.ticker}?symbol={self._data.ticker}"
url = ts_url_base + "&type=" + ",".join([timescale + k for k in keys])
# Yahoo returns maximum 4 years or 5 quarters, regardless of start_dt:
start_dt = datetime.datetime(2016, 12, 31)
end = pd.Timestamp.utcnow().ceil("D")
url += "&period1={}&period2={}".format(int(start_dt.timestamp()), int(end.timestamp()))
url += f"&period1={int(start_dt.timestamp())}&period2={int(end.timestamp())}"
# Step 3: fetch and reshape data
json_str = self._data.cache_get(url=url, proxy=proxy).text

View File

@ -2,6 +2,7 @@ import pandas as pd
from yfinance.data import TickerData
class Holders:
_SCRAPE_URL_ = 'https://finance.yahoo.com/quote'
@ -32,7 +33,7 @@ class Holders:
return self._mutualfund
def _scrape(self, proxy):
ticker_url = "{}/{}".format(self._SCRAPE_URL_, self._data.ticker)
ticker_url = f"{self._SCRAPE_URL_}/{self._data.ticker}"
try:
resp = self._data.cache_get(ticker_url + '/holders', proxy)
holders = pd.read_html(resp.text)

View File

@ -1,10 +1,11 @@
import datetime
import logging
import json
import logging
import warnings
from collections.abc import MutableMapping
import pandas as pd
import numpy as _np
import pandas as pd
from yfinance import utils
from yfinance.data import TickerData
@ -22,7 +23,7 @@ info_retired_keys = info_retired_keys_price | info_retired_keys_exchange | info_
_BASIC_URL_ = "https://query2.finance.yahoo.com/v6/finance/quoteSummary"
from collections.abc import MutableMapping
class InfoDictWrapper(MutableMapping):
""" Simple wrapper around info dict, intercepting 'gets' to
print how-to-migrate messages for specific keys. Requires
@ -125,12 +126,12 @@ class FastInfo:
# Because released before fixing key case, need to officially support
# camel-case but also secretly support snake-case
base_keys = [k for k in _properties if not '_' in k]
base_keys = [k for k in _properties if '_' not in k]
sc_keys = [k for k in _properties if '_' in k]
self._sc_to_cc_key = {k:utils.snake_case_2_camelCase(k) for k in sc_keys}
self._cc_to_sc_key = {v:k for k,v in self._sc_to_cc_key.items()}
self._sc_to_cc_key = {k: utils.snake_case_2_camelCase(k) for k in sc_keys}
self._cc_to_sc_key = {v: k for k, v in self._sc_to_cc_key.items()}
self._public_keys = sorted(base_keys + list(self._sc_to_cc_key.values()))
self._keys = sorted(self._public_keys + sc_keys)
@ -138,37 +139,44 @@ class FastInfo:
# dict imitation:
def keys(self):
return self._public_keys
def items(self):
return [(k,self[k]) for k in self._public_keys]
return [(k, self[k]) for k in self._public_keys]
def values(self):
return [self[k] for k in self._public_keys]
def get(self, key, default=None):
if key in self.keys():
if key in self._cc_to_sc_key:
key = self._cc_to_sc_key[key]
return self[key]
return default
def __getitem__(self, k):
if not isinstance(k, str):
raise KeyError(f"key must be a string")
if not k in self._keys:
if k not in self._keys:
raise KeyError(f"'{k}' not valid key. Examine 'FastInfo.keys()'")
if k in self._cc_to_sc_key:
k = self._cc_to_sc_key[k]
return getattr(self, k)
def __contains__(self, k):
return k in self.keys()
def __iter__(self):
return iter(self.keys())
def __str__(self):
return "lazy-loading dict with keys = " + str(self.keys())
def __repr__(self):
return self.__str__()
def toJSON(self, indent=4):
d = {k:self[k] for k in self.keys()}
return _json.dumps({k:self[k] for k in self.keys()}, indent=indent)
d = {k: self[k] for k in self.keys()}
return json.dumps({k: self[k] for k in self.keys()}, indent=indent)
def _get_1y_prices(self, fullDaysOnly=False):
if self._prices_1y is None:
@ -182,7 +190,7 @@ class FastInfo:
self._today_open = pd.to_datetime(ctp["regular"]["start"], unit='s', utc=True).tz_convert(self.timezone)
self._today_close = pd.to_datetime(ctp["regular"]["end"], unit='s', utc=True).tz_convert(self.timezone)
self._today_midnight = self._today_close.ceil("D")
except:
except Exception:
self._today_open = None
self._today_close = None
self._today_midnight = None
@ -587,9 +595,7 @@ class Quote:
return
self._already_fetched = True
modules = ['financialData', 'quoteType', 'defaultKeyStatistics', 'assetProfile', 'summaryDetail']
params_dict = {}
params_dict["modules"] = modules
params_dict["ssl"] = "true"
params_dict = {"modules": modules, "ssl": "true"}
result = self._data.get_raw_json(
_BASIC_URL_ + f"/{self._data.ticker}", params=params_dict, proxy=proxy
)
@ -611,13 +617,14 @@ class Quote:
if v1
}
# recursively format but only because of 'companyOfficers'
def _format(k, v):
if isinstance(v, dict) and "raw" in v and "fmt" in v:
v2 = v["fmt"] if k in {"regularMarketTime", "postMarketTime"} else v["raw"]
elif isinstance(v, list):
v2 = [_format(None, x) for x in v]
elif isinstance(v, dict):
v2 = {k:_format(k, x) for k, x in v.items()}
v2 = {k: _format(k, x) for k, x in v.items()}
elif isinstance(v, str):
v2 = v.replace("\xa0", " ")
else:
@ -662,8 +669,7 @@ class Quote:
# pass
#
# For just one/few variable is faster to query directly:
url = "https://query1.finance.yahoo.com/ws/fundamentals-timeseries/v1/finance/timeseries/{}?symbol={}".format(
self._data.ticker, self._data.ticker)
url = f"https://query1.finance.yahoo.com/ws/fundamentals-timeseries/v1/finance/timeseries/{self._data.ticker}?symbol={self._data.ticker}"
for k in keys:
url += "&type=" + k
# Request 6 months of data

View File

@ -22,10 +22,10 @@
from __future__ import print_function
import datetime as _datetime
import pandas as _pd
from collections import namedtuple as _namedtuple
import pandas as _pd
from .base import TickerBase
@ -35,15 +35,13 @@ class Ticker(TickerBase):
self._expirations = {}
def __repr__(self):
return 'yfinance.Ticker object <%s>' % self.ticker
return f'yfinance.Ticker object <{self.ticker}>'
def _download_options(self, date=None, proxy=None):
if date is None:
url = "{}/v7/finance/options/{}".format(
self._base_url, self.ticker)
url = f"{self._base_url}/v7/finance/options/{self.ticker}"
else:
url = "{}/v7/finance/options/{}?date={}".format(
self._base_url, self.ticker, date)
url = f"{self._base_url}/v7/finance/options/{self.ticker}?date={date}"
r = self._data.get(url=url, proxy=proxy).json()
if len(r.get('optionChain', {}).get('result', [])) > 0:
@ -84,9 +82,8 @@ class Ticker(TickerBase):
self._download_options()
if date not in self._expirations:
raise ValueError(
"Expiration `%s` cannot be found. "
"Available expiration are: [%s]" % (
date, ', '.join(self._expirations)))
f"Expiration `{date}` cannot be found. "
f"Available expirations are: [{', '.join(self._expirations)}]")
date = self._expirations[date]
options = self._download_options(date, proxy=proxy)

View File

@ -22,19 +22,21 @@
from __future__ import print_function
from . import Ticker, multi
# from collections import namedtuple as _namedtuple
class Tickers:
def __repr__(self):
return 'yfinance.Tickers object <%s>' % ",".join(self.symbols)
return f"yfinance.Tickers object <{','.join(self.symbols)}>"
def __init__(self, tickers, session=None):
tickers = tickers if isinstance(
tickers, list) else tickers.replace(',', ' ').split()
self.symbols = [ticker.upper() for ticker in tickers]
self.tickers = {ticker:Ticker(ticker, session=session) for ticker in self.symbols}
self.tickers = {ticker: Ticker(ticker, session=session) for ticker in self.symbols}
# self.tickers = _namedtuple(
# "Tickers", ticker_objects.keys(), rename=True

View File

@ -21,27 +21,30 @@
from __future__ import print_function
import atexit as _atexit
import datetime as _datetime
import dateutil as _dateutil
import logging
import os as _os
import re as _re
import sqlite3 as _sqlite3
import sys as _sys
import threading
from functools import lru_cache
from inspect import getmembers
from threading import Lock
from types import FunctionType
from typing import Dict, Union, List, Optional
import appdirs as _ad
import numpy as _np
import pandas as _pd
import pytz as _tz
import requests as _requests
import re as _re
import pandas as _pd
import numpy as _np
import sys as _sys
import os as _os
import appdirs as _ad
import sqlite3 as _sqlite3
import atexit as _atexit
from functools import lru_cache
import logging
from threading import Lock
from dateutil.relativedelta import relativedelta
from pytz import UnknownTimeZoneError
from .base import _BASE_URL_
try:
import ujson as _json
except ImportError:
@ -52,14 +55,12 @@ user_agent_headers = {
# From https://stackoverflow.com/a/59128615
from types import FunctionType
from inspect import getmembers
def attributes(obj):
disallowed_names = {
name for name, value in getmembers(type(obj))
name for name, value in getmembers(type(obj))
if isinstance(value, FunctionType)}
return {
name: getattr(obj, name) for name in dir(obj)
name: getattr(obj, name) for name in dir(obj)
if name[0] != '_' and name not in disallowed_names and hasattr(obj, name)}
@ -70,7 +71,7 @@ def print_once(msg):
print(msg)
## Logging
# Logging
# Note: most of this logic is adding indentation with function depth,
# so that DEBUG log is readable.
class IndentLoggerAdapter(logging.LoggerAdapter):
@ -82,20 +83,26 @@ class IndentLoggerAdapter(logging.LoggerAdapter):
msg = '\n'.join([i + m for m in msg.split('\n')])
return msg, kwargs
import threading
_indentation_level = threading.local()
class IndentationContext:
def __init__(self, increment=1):
self.increment = increment
def __enter__(self):
_indentation_level.indent = getattr(_indentation_level, 'indent', 0) + self.increment
def __exit__(self, exc_type, exc_val, exc_tb):
_indentation_level.indent -= self.increment
def get_indented_logger(name=None):
# Never cache the returned value! Will break indentation.
return IndentLoggerAdapter(logging.getLogger(name), {'indent': getattr(_indentation_level, 'indent', 0)})
def log_indent_decorator(func):
def wrapper(*args, **kwargs):
logger = get_indented_logger('yfinance')
@ -109,6 +116,7 @@ def log_indent_decorator(func):
return wrapper
class MultiLineFormatter(logging.Formatter):
# The 'fmt' formatting further down is only applied to first line
# of log message, specifically the padding after %level%.
@ -136,8 +144,11 @@ class MultiLineFormatter(logging.Formatter):
formatted.extend(padding + line for line in lines[1:])
return '\n'.join(formatted)
yf_logger = None
yf_log_indented = False
def get_yf_logger():
global yf_logger
if yf_logger is None:
@ -147,6 +158,7 @@ def get_yf_logger():
yf_logger = get_indented_logger('yfinance')
return yf_logger
def setup_debug_formatting():
global yf_logger
yf_logger = get_yf_logger()
@ -165,24 +177,21 @@ def setup_debug_formatting():
global yf_log_indented
yf_log_indented = True
def enable_debug_mode():
get_yf_logger().setLevel(logging.DEBUG)
setup_debug_formatting()
##
def is_isin(string):
return bool(_re.match("^([A-Z]{2})([A-Z0-9]{9})([0-9]{1})$", string))
return bool(_re.match("^([A-Z]{2})([A-Z0-9]{9})([0-9])$", string))
def get_all_by_isin(isin, proxy=None, session=None):
if not (is_isin(isin)):
raise ValueError("Invalid ISIN number")
from .base import _BASE_URL_
session = session or _requests
url = "{}/v1/finance/search?q={}".format(_BASE_URL_, isin)
url = f"{_BASE_URL_}/v1/finance/search?q={isin}"
data = session.get(url=url, proxies=proxy, headers=user_agent_headers)
try:
data = data.json()
@ -234,7 +243,7 @@ def empty_earnings_dates_df():
def build_template(data):
'''
"""
build_template returns the details required to rebuild any of the yahoo finance financial statements in the same order as the yahoo finance webpage. The function is built to be used on the "FinancialTemplateStore" json which appears in any one of the three yahoo finance webpages: "/financials", "/cash-flow" and "/balance-sheet".
Returns:
@ -243,95 +252,80 @@ def build_template(data):
- template_order: The order that quarterlies should be in (note that quarterlies have no pre-fix - hence why this is required).
- level_detail: The level of each individual line item. E.g. for the "/financials" webpage, "Total Revenue" is a level 0 item and is the summation of "Operating Revenue" and "Excise Taxes" which are level 1 items.
'''
"""
template_ttm_order = [] # Save the TTM (Trailing Twelve Months) ordering to an object.
template_annual_order = [] # Save the annual ordering to an object.
template_order = [] # Save the ordering to an object (this can be utilized for quarterlies)
level_detail = [] # Record the level of each line item of the income statement ("Operating Revenue" and "Excise Taxes" sum to return "Total Revenue" we need to keep track of this)
for key in data['template']:
# Loop through the json to retreive the exact financial order whilst appending to the objects
template_ttm_order.append('trailing{}'.format(key['key']))
template_annual_order.append('annual{}'.format(key['key']))
template_order.append('{}'.format(key['key']))
level_detail.append(0)
if 'children' in key:
for child1 in key['children']: # Level 1
template_ttm_order.append('trailing{}'.format(child1['key']))
template_annual_order.append('annual{}'.format(child1['key']))
template_order.append('{}'.format(child1['key']))
level_detail.append(1)
if 'children' in child1:
for child2 in child1['children']: # Level 2
template_ttm_order.append('trailing{}'.format(child2['key']))
template_annual_order.append('annual{}'.format(child2['key']))
template_order.append('{}'.format(child2['key']))
level_detail.append(2)
if 'children' in child2:
for child3 in child2['children']: # Level 3
template_ttm_order.append('trailing{}'.format(child3['key']))
template_annual_order.append('annual{}'.format(child3['key']))
template_order.append('{}'.format(child3['key']))
level_detail.append(3)
if 'children' in child3:
for child4 in child3['children']: # Level 4
template_ttm_order.append('trailing{}'.format(child4['key']))
template_annual_order.append('annual{}'.format(child4['key']))
template_order.append('{}'.format(child4['key']))
level_detail.append(4)
if 'children' in child4:
for child5 in child4['children']: # Level 5
template_ttm_order.append('trailing{}'.format(child5['key']))
template_annual_order.append('annual{}'.format(child5['key']))
template_order.append('{}'.format(child5['key']))
level_detail.append(5)
def traverse(node, level):
"""
A recursive function that visits a node and its children.
Args:
node: The current node in the data structure.
level: The depth of the current node in the data structure.
"""
if level > 5: # Stop when level is above 5
return
template_ttm_order.append(f"trailing{node['key']}")
template_annual_order.append(f"annual{node['key']}")
template_order.append(f"{node['key']}")
level_detail.append(level)
if 'children' in node: # Check if the node has children
for child in node['children']: # If yes, traverse each child
traverse(child, level + 1) # Increment the level by 1 for each child
for key in data['template']: # Loop through the data
traverse(key, 0) # Call the traverse function with initial level being 0
return template_ttm_order, template_annual_order, template_order, level_detail
def retreive_financial_details(data):
'''
retreive_financial_details returns all of the available financial details under the "QuoteTimeSeriesStore" for any of the following three yahoo finance webpages: "/financials", "/cash-flow" and "/balance-sheet".
def retrieve_financial_details(data):
"""
retrieve_financial_details returns all of the available financial details under the
"QuoteTimeSeriesStore" for any of the following three yahoo finance webpages:
"/financials", "/cash-flow" and "/balance-sheet".
Returns:
- TTM_dicts: A dictionary full of all of the available Trailing Twelve Month figures, this can easily be converted to a pandas dataframe.
- Annual_dicts: A dictionary full of all of the available Annual figures, this can easily be converted to a pandas dataframe.
'''
"""
TTM_dicts = [] # Save a dictionary object to store the TTM financials.
Annual_dicts = [] # Save a dictionary object to store the Annual financials.
for key in data['timeSeries']: # Loop through the time series data to grab the key financial figures.
for key, timeseries in data.get('timeSeries', {}).items(): # Loop through the time series data to grab the key financial figures.
try:
if len(data['timeSeries'][key]) > 0:
time_series_dict = {}
time_series_dict['index'] = key
for each in data['timeSeries'][key]: # Loop through the years
if each == None:
if timeseries:
time_series_dict = {'index': key}
for each in timeseries: # Loop through the years
if not each:
continue
else:
time_series_dict[each['asOfDate']] = each['reportedValue']
# time_series_dict["{}".format(each['asOfDate'])] = data['timeSeries'][key][each]['reportedValue']
time_series_dict[each.get('asOfDate')] = each.get('reportedValue')
if 'trailing' in key:
TTM_dicts.append(time_series_dict)
elif 'annual' in key:
Annual_dicts.append(time_series_dict)
except Exception as e:
pass
except KeyError as e:
print(f"An error occurred while processing the key: {e}")
return TTM_dicts, Annual_dicts
def format_annual_financial_statement(level_detail, annual_dicts, annual_order, ttm_dicts=None, ttm_order=None):
'''
"""
format_annual_financial_statement formats any annual financial statement
Returns:
- _statement: A fully formatted annual financial statement in pandas dataframe.
'''
"""
Annual = _pd.DataFrame.from_dict(annual_dicts).set_index("index")
Annual = Annual.reindex(annual_order)
Annual.index = Annual.index.str.replace(r'annual', '')
# Note: balance sheet is the only financial statement with no ttm detail
if (ttm_dicts not in [[], None]) and (ttm_order not in [[], None]):
TTM = _pd.DataFrame.from_dict(ttm_dicts).set_index("index")
TTM = TTM.reindex(ttm_order)
if ttm_dicts and ttm_order:
TTM = _pd.DataFrame.from_dict(ttm_dicts).set_index("index").reindex(ttm_order)
# Add 'TTM' prefix to all column names, so if combined we can tell
# the difference between actuals and TTM (similar to yahoo finance).
TTM.columns = ['TTM ' + str(col) for col in TTM.columns]
@ -349,12 +343,12 @@ def format_annual_financial_statement(level_detail, annual_dicts, annual_order,
def format_quarterly_financial_statement(_statement, level_detail, order):
'''
"""
format_quarterly_financial_statements formats any quarterly financial statement
Returns:
- _statement: A fully formatted quarterly financial statement in pandas dataframe.
'''
"""
_statement = _statement.reindex(order)
_statement.index = camel2title(_statement.T)
_statement['level_detail'] = level_detail
@ -405,7 +399,7 @@ def camel2title(strings: List[str], sep: str = ' ', acronyms: Optional[List[str]
# Apply str.title() to non-acronym words
strings = [s.split(sep) for s in strings]
strings = [[j.title() if not j in acronyms else j for j in s] for s in strings]
strings = [[j.title() if j not in acronyms else j for j in s] for s in strings]
strings = [sep.join(s) for s in strings]
return strings
@ -435,14 +429,14 @@ def _parse_user_dt(dt, exchange_tz):
def _interval_to_timedelta(interval):
if interval == "1mo":
return _dateutil.relativedelta.relativedelta(months=1)
return relativedelta(months=1)
elif interval == "3mo":
return _dateutil.relativedelta.relativedelta(months=3)
return relativedelta(months=3)
elif interval == "1y":
return _dateutil.relativedelta.relativedelta(years=1)
return relativedelta(years=1)
elif interval == "1wk":
return _pd.Timedelta(days=7)
else:
else:
return _pd.Timedelta(interval)
@ -542,8 +536,7 @@ def parse_actions(data):
splits.set_index("date", inplace=True)
splits.index = _pd.to_datetime(splits.index, unit="s")
splits.sort_index(inplace=True)
splits["Stock Splits"] = splits["numerator"] / \
splits["denominator"]
splits["Stock Splits"] = splits["numerator"] / splits["denominator"]
splits = splits[["Stock Splits"]]
if dividends is None:
@ -622,7 +615,7 @@ def fix_Yahoo_returning_live_separate(quotes, interval, tz_exchange):
elif interval == "3mo":
last_rows_same_interval = dt1.year == dt2.year and dt1.quarter == dt2.quarter
else:
last_rows_same_interval = (dt1-dt2) < _pd.Timedelta(interval)
last_rows_same_interval = (dt1 - dt2) < _pd.Timedelta(interval)
if last_rows_same_interval:
# Last two rows are within same interval
@ -678,12 +671,12 @@ def safe_merge_dfs(df_main, df_sub, interval):
df_main = df_main.drop('_date', axis=1)
df_sub = df_sub.drop('_date', axis=1)
else:
indices = _np.searchsorted(_np.append(df_main.index, df_main.index[-1]+td), df_sub.index, side='right')
indices = _np.searchsorted(_np.append(df_main.index, df_main.index[-1] + td), df_sub.index, side='right')
indices -= 1 # Convert from [[i-1], [i]) to [[i], [i+1])
# Numpy.searchsorted does not handle out-of-range well, so handle manually:
for i in range(len(df_sub.index)):
dt = df_sub.index[i]
if dt < df_main.index[0] or dt >= df_main.index[-1]+td:
if dt < df_main.index[0] or dt >= df_main.index[-1] + td:
# Out-of-range
indices[i] = -1
@ -695,23 +688,23 @@ def safe_merge_dfs(df_main, df_sub, interval):
next_interval_start_dt = last_dt + td
if interval == '1d':
# Allow for weekends & holidays
next_interval_end_dt = last_dt+7*_pd.Timedelta(days=7)
next_interval_end_dt = last_dt + 7 * _pd.Timedelta(days=7)
else:
next_interval_end_dt = next_interval_start_dt + td
for i in _np.where(f_outOfRange)[0]:
dt = df_sub.index[i]
if dt >= next_interval_start_dt and dt < next_interval_end_dt:
if next_interval_start_dt <= dt < next_interval_end_dt:
new_dt = dt if interval == '1d' else next_interval_start_dt
get_yf_logger().debug(f"Adding out-of-range {data_col} @ {dt.date()} in new prices row of NaNs")
df_main.loc[new_dt] = _np.nan
# Re-calculate indices
indices = _np.searchsorted(_np.append(df_main.index, df_main.index[-1]+td), df_sub.index, side='right')
indices = _np.searchsorted(_np.append(df_main.index, df_main.index[-1] + td), df_sub.index, side='right')
indices -= 1 # Convert from [[i-1], [i]) to [[i], [i+1])
# Numpy.searchsorted does not handle out-of-range well, so handle manually:
for i in range(len(df_sub.index)):
dt = df_sub.index[i]
if dt < df_main.index[0] or dt >= df_main.index[-1]+td:
if dt < df_main.index[0] or dt >= df_main.index[-1] + td:
# Out-of-range
indices[i] = -1
@ -740,10 +733,11 @@ def safe_merge_dfs(df_main, df_sub, interval):
df = df.groupby("_NewIndex").prod()
df.index.name = None
else:
raise Exception("New index contains duplicates but unsure how to aggregate for '{}'".format(data_col_name))
raise Exception(f"New index contains duplicates but unsure how to aggregate for '{data_col_name}'")
if "_NewIndex" in df.columns:
df = df.drop("_NewIndex", axis=1)
return df
new_index = df_main.index[indices]
df_sub = _reindex_events(df_sub, new_index, data_col)
@ -802,7 +796,7 @@ def format_history_metadata(md, tradingPeriodsOnly=True):
if "tradingPeriods" in md:
tps = md["tradingPeriods"]
if tps == {"pre":[], "post":[]}:
if tps == {"pre": [], "post": []}:
# Ignore
pass
elif isinstance(tps, (list, dict)):
@ -818,8 +812,8 @@ def format_history_metadata(md, tradingPeriodsOnly=True):
post_df = _pd.DataFrame.from_records(_np.hstack(tps["post"]))
regular_df = _pd.DataFrame.from_records(_np.hstack(tps["regular"]))
pre_df = pre_df.rename(columns={"start":"pre_start", "end":"pre_end"}).drop(["timezone", "gmtoffset"], axis=1)
post_df = post_df.rename(columns={"start":"post_start", "end":"post_end"}).drop(["timezone", "gmtoffset"], axis=1)
pre_df = pre_df.rename(columns={"start": "pre_start", "end": "pre_end"}).drop(["timezone", "gmtoffset"], axis=1)
post_df = post_df.rename(columns={"start": "post_start", "end": "post_end"}).drop(["timezone", "gmtoffset"], axis=1)
regular_df = regular_df.drop(["timezone", "gmtoffset"], axis=1)
cols = ["pre_start", "pre_end", "start", "end", "post_start", "post_end"]
@ -836,6 +830,7 @@ def format_history_metadata(md, tradingPeriodsOnly=True):
return md
class ProgressBar:
def __init__(self, iterations, text='completed'):
self.text = text
@ -868,19 +863,16 @@ class ProgressBar:
def update_iteration(self, val=None):
val = val if val is not None else self.elapsed / float(self.iterations)
self.__update_amount(val * 100.0)
self.prog_bar += ' %s of %s %s' % (
self.elapsed, self.iterations, self.text)
self.prog_bar += f" {self.elapsed} of {self.iterations} {self.text}"
def __update_amount(self, new_amount):
percent_done = int(round((new_amount / 100.0) * 100.0))
all_full = self.width - 2
num_hashes = int(round((percent_done / 100.0) * all_full))
self.prog_bar = '[' + self.fill_char * \
num_hashes + ' ' * (all_full - num_hashes) + ']'
self.prog_bar = '[' + self.fill_char * num_hashes + ' ' * (all_full - num_hashes) + ']'
pct_place = (len(self.prog_bar) // 2) - len(str(percent_done))
pct_string = '%d%%' % percent_done
self.prog_bar = self.prog_bar[0:pct_place] + \
(pct_string + self.prog_bar[pct_place + len(pct_string):])
pct_string = f'{percent_done}%%'
self.prog_bar = self.prog_bar[0:pct_place] + (pct_string + self.prog_bar[pct_place + len(pct_string):])
def __str__(self):
return str(self.prog_bar)
@ -891,7 +883,7 @@ class ProgressBar:
# ---------------------------------
class _KVStore:
"""Simpel Sqlite backed key/value store, key and value are strings. Should be thread safe."""
"""Simple Sqlite backed key/value store, key and value are strings. Should be thread safe."""
def __init__(self, filename):
self._cache_mutex = Lock()
@ -958,8 +950,7 @@ class _TzCache:
try:
self._tz_db = _KVStore(_os.path.join(self._db_dir, "tkr-tz.db"))
except _sqlite3.DatabaseError as err:
raise _TzCacheException("Error creating TzCache folder: '{}' reason: {}"
.format(self._db_dir, err))
raise _TzCacheException(f"Error creating TzCache folder: '{self._db_dir}' reason: {err}")
self._migrate_cache_tkr_tz()
def _setup_cache_folder(self):
@ -967,12 +958,10 @@ class _TzCache:
try:
_os.makedirs(self._db_dir)
except OSError as err:
raise _TzCacheException("Error creating TzCache folder: '{}' reason: {}"
.format(self._db_dir, err))
raise _TzCacheException(f"Error creating TzCache folder: '{self._db_dir}' reason: {err}")
elif not (_os.access(self._db_dir, _os.R_OK) and _os.access(self._db_dir, _os.W_OK)):
raise _TzCacheException("Cannot read and write in TzCache folder: '{}'"
.format(self._db_dir, ))
raise _TzCacheException(f"Cannot read and write in TzCache folder: '{self._db_dir}'")
def lookup(self, tkr):
return self.tz_db.get(tkr)
@ -981,7 +970,7 @@ class _TzCache:
if tz is None:
self.tz_db.delete(tkr)
elif self.tz_db.get(tkr) is not None:
raise Exception("Tkr {} tz already in cache".format(tkr))
raise Exception(f"Tkr {tkr} tz already in cache")
else:
self.tz_db.set(tkr, tz)
@ -1009,7 +998,7 @@ class _TzCache:
else:
# Discard corrupt data:
df = df[~df["Tz"].isna().to_numpy()]
df = df[~(df["Tz"]=='').to_numpy()]
df = df[~(df["Tz"] == '').to_numpy()]
df = df[~df.index.isna()]
if not df.empty:
try:
@ -1048,10 +1037,9 @@ def get_tz_cache():
try:
_tz_cache = _TzCache()
except _TzCacheException as err:
get_yf_logger().info("Failed to create TzCache, reason: %s. "
"TzCache will not be used. "
"Tip: You can direct cache to use a different location with 'set_tz_cache_location(mylocation)'",
err)
get_yf_logger().info(f"Failed to create TzCache, reason: {err}. "
"TzCache will not be used. "
"Tip: You can direct cache to use a different location with 'set_tz_cache_location(mylocation)'")
_tz_cache = _TzCacheDummy()
return _tz_cache