diff --git a/src/__mocks__/api/v3/client.ts b/src/__mocks__/api/v3/client.ts new file mode 100644 index 0000000..78ef5f0 --- /dev/null +++ b/src/__mocks__/api/v3/client.ts @@ -0,0 +1,35 @@ +import { default as company } from '~/__mocks__/fixtures/v3/company.js' +import { default as daily } from '~/__mocks__/fixtures/v3/daily.js' +import { default as quarter } from '~/__mocks__/fixtures/v3/quarter.js' + +export class BuffettCodeApiClientV2 { + public mockCompany = jest.fn() + public mockDaily = jest.fn() + public mockQuarter = jest.fn() + + constructor(readonly token: string) { + this.mockCompany.mockReturnValue(company) + this.mockDaily.mockReturnValue(daily) + this.mockQuarter.mockReturnValue(quarter) + } + + company(ticker): object | null { + const company = this.mockCompany()[ticker] + return company ? company[0] : null + } + + quarter(ticker): object | null { + const quarter = this.mockQuarter()[ticker] + return quarter ? quarter[0] : null + } + + daily(ticker): object | null { + const daily = this.mockDaily()[ticker] + return daily ? daily[0] : null + } + + ondemandQuarter(ticker): object | null { + const quarter = this.mockQuarter()[ticker] + return quarter ? quarter[0] : null + } +} diff --git a/src/__mocks__/fixtures/v3/company.js b/src/__mocks__/fixtures/v3/company.js new file mode 100644 index 0000000..5256734 --- /dev/null +++ b/src/__mocks__/fixtures/v3/company.js @@ -0,0 +1,95 @@ +module.exports = { + 'data': { + tosyo_33category: 'サービス業', + url: 'http://corporate.kakaku.com/', + company_name: 'カカクコム', + company_name_en: 'Kakaku.com Inc.', + fiscal_month: 3.0, + established_date: '1997/12', + listing_date: '2003/10/09', + priority_market: 't1', + accounting_standard: 'IFRS', + latest_fiscal_year: 2020.0, + latest_fiscal_quarter: 1.0, + oldest_fiscal_year: 2008.0, + oldest_fiscal_quarter: 4.0, + fixed_tier_range: { + oldest_fiscal_year: 2015.0, + oldest_fiscal_quarter: 1.0, + latest_fiscal_year: 2020.0, + latest_fiscal_quarter: 1.0 + } + }, + column_description: { + tosyo_33category: { + name_jp: '東証33業種', + unit: 'なし' + }, + url: { + name_jp: '会社URL', + unit: 'なし' + }, + company_name: { + name_jp: '会社名', + unit: 'なし' + }, + company_name_en: { + name_jp: '会社英名', + unit: 'なし' + }, + fiscal_month: { + name_jp: '決算月', + unit: 'なし' + }, + established_date: { + name_jp: '設立日', + unit: 'なし' + }, + listing_date: { + name_jp: '上場日', + unit: 'なし' + }, + priority_market: { + name_jp: '優先市場', + unit: 'なし' + }, + accounting_standard: { + name_jp: '会計基準', + unit: 'なし' + }, + latest_fiscal_year: { + name_jp: '直近決算年度', + unit: 'なし' + }, + latest_fiscal_quarter: { + name_jp: '直近決算四半期', + unit: 'なし' + }, + oldest_fiscal_year: { + name_jp: '取得可能な最古の決算年度', + unit: 'なし' + }, + oldest_fiscal_quarter: { + name_jp: '取得可能な最古の決算四半期', + unit: 'なし' + }, + fixed_tier_range: { + oldest_fiscal_year: { + name_jp: '定額利用可能な下限の決算年度', + unit: 'なし' + }, + oldest_fiscal_quarter: { + name_jp: '定額利用可能な下限の決算四半期', + unit: 'なし' + }, + latest_fiscal_year: { + name_jp: '定額利用可能な上限の決算年度', + unit: 'なし' + }, + latest_fiscal_quarter: { + name_jp: '定額利用可能な上限の決算四半期', + unit: 'なし' + } + } + } +} diff --git a/src/__mocks__/fixtures/v3/daily-property.js b/src/__mocks__/fixtures/v3/daily-property.js new file mode 100644 index 0000000..614382d --- /dev/null +++ b/src/__mocks__/fixtures/v3/daily-property.js @@ -0,0 +1,82 @@ +module.exports = { + cash_market_capital_ratio: { + name_jp: 'キャッシュ時価総額比率', + unit: '%' + }, + debt_market_capital_ratio: { + name_jp: '有利子負債/時価総額比率', + unit: '倍' + }, + dividend_yield_actual: { + name_jp: '配当利回り(実績)', + unit: '%' + }, + dividend_yield_forecast: { + name_jp: '配当利回り(会社予想)', + unit: '%' + }, + ebitda_forecast: { + name_jp: 'EBITDA(会社予想)', + unit: '百万円' + }, + enterprise_value: { + name_jp: '企業価値', + unit: '百万円' + }, + eps_forecast: { + name_jp: 'EPS(会社予想)', + unit: '円' + }, + ev_ebitda_forecast: { + name_jp: 'EV/EBITDA(会社予想)', + unit: '倍' + }, + ex_dividend: { + name_jp: '配当金(会社予想)', + unit: '円' + }, + listing_years: { + name_jp: '上場年数', + unit: '年' + }, + market_capital: { + name_jp: '時価総額', + unit: '百万円' + }, + net_income_growth_rate_forecast: { + name_jp: '純利益成長率(直近年度実績→会社予想)', + unit: '%' + }, + net_sales_growth_rate_forecast: { + name_jp: '売上高成長率(直近年度実績→会社予想)', + unit: '%' + }, + operating_income_growth_rate_forecast: { + name_jp: '営業利益成長率(直近年度実績→会社予想)', + unit: '%' + }, + pbr: { + name_jp: 'PBR', + unit: '倍' + }, + pcfr_forecast: { + name_jp: 'PCFR', + unit: '倍' + }, + per_forecast: { + name_jp: 'PER(会社予想)', + unit: '倍' + }, + per_pbr: { + name_jp: 'PER×PBR', + unit: '単位無し' + }, + psr_forecast: { + name_jp: 'PSR(会社予想)', + unit: '倍' + }, + trading_volume: { + name_jp: '出来高', + unit: '株' + } +} diff --git a/src/__mocks__/fixtures/v3/daily.js b/src/__mocks__/fixtures/v3/daily.js new file mode 100644 index 0000000..497fe6f --- /dev/null +++ b/src/__mocks__/fixtures/v3/daily.js @@ -0,0 +1,106 @@ +module.exports = { + column_description: { + cash_market_capital_ratio: { + name_jp: 'キャッシュ時価総額比率', + unit: '%' + }, + debt_market_capital_ratio: { + name_jp: '有利子負債/時価総額比率', + unit: '倍' + }, + dividend_yield_actual: { + name_jp: '配当利回り(実績)', + unit: '%' + }, + dividend_yield_forecast: { + name_jp: '配当利回り(会社予想)', + unit: '%' + }, + ebitda_forecast: { + name_jp: 'EBITDA(会社予想)', + unit: '百万円' + }, + enterprise_value: { + name_jp: '企業価値', + unit: '百万円' + }, + eps_forecast: { + name_jp: 'EPS(会社予想)', + unit: '円' + }, + ev_ebitda_forecast: { + name_jp: 'EV/EBITDA(会社予想)', + unit: '倍' + }, + ex_dividend: { + name_jp: '配当金(会社予想)', + unit: '円' + }, + listing_years: { + name_jp: '上場年数', + unit: '年' + }, + market_capital: { + name_jp: '時価総額', + unit: '百万円' + }, + net_income_growth_rate_forecast: { + name_jp: '純利益成長率(直近年度実績→会社予想)', + unit: '%' + }, + net_sales_growth_rate_forecast: { + name_jp: '売上高成長率(直近年度実績→会社予想)', + unit: '%' + }, + operating_income_growth_rate_forecast: { + name_jp: '営業利益成長率(直近年度実績→会社予想)', + unit: '%' + }, + pbr: { + name_jp: 'PBR', + unit: '倍' + }, + pcfr_forecast: { + name_jp: 'PCFR', + unit: '倍' + }, + per_forecast: { + name_jp: 'PER(会社予想)', + unit: '倍' + }, + per_pbr: { + name_jp: 'PER×PBR', + unit: '単位無し' + }, + psr_forecast: { + name_jp: 'PSR(会社予想)', + unit: '倍' + }, + trading_volume: { + name_jp: '出来高', + unit: '株' + } + }, + data: { + cash_market_capital_ratio: 4.98944759676063, + debt_market_capital_ratio: 1.48298604408628, + dividend_yield_actual: 1.40154169586545, + dividend_yield_forecast: 1.40154169586545, + ebitda_forecast: null, + enterprise_value: 567189541668.0, + eps_forecast: 0.0, + ev_ebitda_forecast: null, + ex_dividend: 40.0, + listing_years: 17.0, + market_capital: 587800541668.0, + net_income_growth_rate_forecast: -100.0, + net_sales_growth_rate_forecast: -100.0, + operating_income_growth_rate_forecast: -100.0, + pbr: 13.3959420603934, + pcfr_forecast: 182.603461220255, + per_forecast: null, + per_pbr: null, + psr_forecast: null, + trading_volume: 437700.0 + } +} diff --git a/src/__mocks__/fixtures/v3/quarter-property.js b/src/__mocks__/fixtures/v3/quarter-property.js new file mode 100644 index 0000000..d284384 --- /dev/null +++ b/src/__mocks__/fixtures/v3/quarter-property.js @@ -0,0 +1,746 @@ +module.exports = { + company_name: { + name_jp: '社名', + unit: 'なし' + }, + ceo_name: { + name_jp: '代表者名', + unit: 'なし' + }, + headquarters_address: { + name_jp: '所在地', + unit: 'なし' + }, + end_date: { + name_jp: '有価証券報告書の期末日', + unit: '日付' + }, + edinet_updated_date: { + name_jp: 'edinet発行日', + unit: '日付' + }, + tdnet_updated_date: { + name_jp: 'tdnet発行日', + unit: '日付' + }, + edinet_title: { + name_jp: 'edinet開示資料名', + unit: 'なし' + }, + tdnet_title: { + name_jp: 'tdnet開示資料名', + unit: 'なし' + }, + accounting_standard: { + name_jp: '会計基準', + unit: 'なし' + }, + num_of_shares: { + name_jp: '株式総数', + unit: '株' + }, + issued_share_num: { + name_jp: '発行済株式総数', + unit: '株' + }, + treasury_stock_num: { + name_jp: '自己株数', + unit: '株' + }, + assets: { + name_jp: '総資産', + unit: '百万円' + }, + current_assets: { + name_jp: '流動資産', + unit: '百万円' + }, + cash_and_deposits: { + name_jp: '現預金', + unit: '百万円' + }, + trade_receivables: { + name_jp: '売上債権', + unit: '百万円' + }, + notes_accounts_receivable: { + name_jp: '受取手形および売掛金', + unit: '百万円' + }, + notes_receivable: { + name_jp: '受取手形', + unit: '百万円' + }, + accounts_receivable: { + name_jp: '売掛金', + unit: '百万円' + }, + current_securities: { + name_jp: '有価証券', + unit: '百万円' + }, + inventories: { + name_jp: '棚卸資産', + unit: '百万円' + }, + merchandise: { + name_jp: '商品', + unit: '百万円' + }, + work_in_process: { + name_jp: '仕掛品', + unit: '百万円' + }, + raw_materials_and_supplies: { + name_jp: '原材料', + unit: '百万円' + }, + prepaid_expenses: { + name_jp: '前払金', + unit: '百万円' + }, + current_dta: { + name_jp: '繰延税金資産(流動)', + unit: '百万円' + }, + current_allowance_doubtful_accounts: { + name_jp: '貸倒引当金', + unit: '百万円' + }, + non_current_assets: { + name_jp: '固定資産', + unit: '百万円' + }, + tangible_fixed_assets: { + name_jp: '有形固定資産', + unit: '百万円' + }, + buildings: { + name_jp: '建物・構築物', + unit: '百万円' + }, + machineries: { + name_jp: '機械・運搬具・工具', + unit: '百万円' + }, + land: { + name_jp: '土地', + unit: '百万円' + }, + construction_in_progress: { + name_jp: '建設仮勘定', + unit: '百万円' + }, + intangible_assets: { + name_jp: '無形固定資産', + unit: '百万円' + }, + good_will: { + name_jp: 'のれん', + unit: '百万円' + }, + investments_and_other_assets: { + name_jp: '投資その他の資産', + unit: '百万円' + }, + investment_securities: { + name_jp: '投資有価証券', + unit: '百万円' + }, + non_current_dta: { + name_jp: '繰延税金資産(固定)', + unit: '百万円' + }, + non_current_allowance_doubtful_accounts: { + name_jp: '貸倒引当金', + unit: '百万円' + }, + lease_and_guarantee_deposits: { + name_jp: '敷金および保証金', + unit: '百万円' + }, + liabilities: { + name_jp: '負債', + unit: '百万円' + }, + debt: { + name_jp: '有利子負債', + unit: '百万円' + }, + current_liabilities: { + name_jp: '流動負債', + unit: '百万円' + }, + trade_payables: { + name_jp: '仕入債務', + unit: '百万円' + }, + notes_accounts_payable: { + name_jp: '支払手形および買掛金', + unit: '百万円' + }, + accounts_payable: { + name_jp: '買掛金', + unit: '百万円' + }, + notes_payable: { + name_jp: '支払手形', + unit: '百万円' + }, + short_term_bonds_payable: { + name_jp: '短期社債', + unit: '百万円' + }, + short_term_loans_payable: { + name_jp: '短期借入金', + unit: '百万円' + }, + commercial_papers_liabilities: { + name_jp: 'コマーシャルペーパー', + unit: '百万円' + }, + current_lease_obligations: { + name_jp: 'リース債務(流動負債)', + unit: '百万円' + }, + current_portion_of_long_term_loans: { + name_jp: '1年以内返済の長期借入金', + unit: '百万円' + }, + current_portion_of_bonds: { + name_jp: '1年以内返済の社債', + unit: '百万円' + }, + current_portion_of_convertible_bonds: { + name_jp: '1年以内返済の転換社債', + unit: '百万円' + }, + current_portion_of_bonds_with_subscription_rights: { + name_jp: '1年以内返済の新株予約権付社債', + unit: '百万円' + }, + advances_received: { + name_jp: '前受金', + unit: '百万円' + }, + corporate_tax_payable: { + name_jp: '未払法人税等', + unit: '百万円' + }, + non_current_liabilities: { + name_jp: '固定負債', + unit: '百万円' + }, + bonds_payable: { + name_jp: '社債', + unit: '百万円' + }, + convertible_bonds: { + name_jp: '転換社債', + unit: '百万円' + }, + convertible_bond_type_bonds_with_subscription_rights: { + name_jp: '新株予約権付転換社債', + unit: '百万円' + }, + non_current_bonds_with_subscription_right: { + name_jp: '新株予約権付社債', + unit: '百万円' + }, + long_term_loans_payable: { + name_jp: '長期借入金', + unit: '百万円' + }, + non_current_lease_obligations: { + name_jp: 'リース債務(固定負債)', + unit: '百万円' + }, + non_current_dtl: { + name_jp: '繰延税金負債(固定)', + unit: '百万円' + }, + net_assets: { + name_jp: '純資産', + unit: '百万円' + }, + shareholders_equity: { + name_jp: '株主資本', + unit: '百万円' + }, + capital_stock: { + name_jp: '資本金', + unit: '百万円' + }, + additional_capital_stock: { + name_jp: '資本剰余金', + unit: '百万円' + }, + retained_earnings: { + name_jp: '利益剰余金', + unit: '百万円' + }, + treasury_stock: { + name_jp: '自己株式', + unit: '百万円' + }, + valuation_and_translation_adjustments: { + name_jp: '評価換算差額等', + unit: '百万円' + }, + non_controlling_interests: { + name_jp: '非支配持分', + unit: '百万円' + }, + net_sales: { + name_jp: '売上', + unit: '百万円' + }, + cost_of_sales: { + name_jp: '売上原価', + unit: '百万円' + }, + gross_profit: { + name_jp: '売上高総利益', + unit: '百万円' + }, + gross_margin: { + name_jp: '売上高総利益率', + unit: '%' + }, + sga: { + name_jp: '販売費および一般管理費', + unit: '百万円' + }, + operating_income: { + name_jp: '営業利益', + unit: '百万円' + }, + operating_margin: { + name_jp: '営業利益率', + unit: '%' + }, + non_operating_income: { + name_jp: '営業外収益', + unit: '百万円' + }, + interest_and_dividends_income: { + name_jp: '受取利息および受取配当金', + unit: '百万円' + }, + interest_income: { + name_jp: '受取利息', + unit: '百万円' + }, + dividends_income: { + name_jp: '受取配当金', + unit: '百万円' + }, + equity_method_income: { + name_jp: '持分法による投資利益', + unit: '百万円' + }, + non_operating_expenses: { + name_jp: '営業外費用', + unit: '百万円' + }, + interest_expense: { + name_jp: '支払利息', + unit: '百万円' + }, + equity_method_loss: { + name_jp: '持分法による投資損失', + unit: '百万円' + }, + ordinary_income: { + name_jp: '経常利益', + unit: '百万円' + }, + extraordinary_income: { + name_jp: '特別利益', + unit: '百万円' + }, + gain_of_sales_non_current_assets: { + name_jp: '固定資産売却益', + unit: '百万円' + }, + gain_of_sales_investment_securities: { + name_jp: '投資有価証券売却益', + unit: '百万円' + }, + extraordinary_loss: { + name_jp: '特別損失', + unit: '百万円' + }, + loss_of_sales_non_current_assets: { + name_jp: '固定資産売却損', + unit: '百万円' + }, + loss_of_valuation_investment_securities: { + name_jp: '投資有価証券売却損', + unit: '百万円' + }, + impairment_loss: { + name_jp: '減損損失', + unit: '百万円' + }, + income_before_income_taxes: { + name_jp: '税引前当期純利益', + unit: '百万円' + }, + income_taxes: { + name_jp: '法人税等', + unit: '百万円' + }, + real_corporate_tax_rate: { + name_jp: '実質法人税率', + unit: '%' + }, + net_income: { + name_jp: '当期純利益', + unit: '百万円' + }, + non_controling_interests: { + name_jp: '非支配株主に帰属する当期純利益', + unit: '百万円' + }, + profit_loss_attributable_to_owners_of_parent: { + name_jp: '親会社株主に帰属する当期純利益', + unit: '百万円' + }, + net_profit_margin: { + name_jp: '当期純利益率', + unit: '%' + }, + operating_cash_flow: { + name_jp: '営業キャッシュフロー', + unit: '百万円' + }, + income_before_taxes: { + name_jp: '税金等調整前当期純利益', + unit: '百万円' + }, + depreciation: { + name_jp: '減価償却費', + unit: '百万円' + }, + amortization: { + name_jp: 'のれん償却費', + unit: '百万円' + }, + decrease_trade_receivables_op_cf: { + name_jp: '売上債権の増減額', + unit: '百万円' + }, + decrease_inventories_op_cf: { + name_jp: '棚卸資産の増減額', + unit: '百万円' + }, + increase_trade_payables_op_cf: { + name_jp: '仕入債務の増減額', + unit: '百万円' + }, + investment_cash_flow: { + name_jp: '投資キャッシュフロー', + unit: '百万円' + }, + purchase_of_property: { + name_jp: '有形固定資産の取得による支出', + unit: '百万円' + }, + sale_of_property: { + name_jp: '有形固定資産の売却による収入', + unit: '百万円' + }, + purchase_of_intangible_assets: { + name_jp: '無形固定資産の取得による支出', + unit: '百万円' + }, + sale_of_intangible_assets: { + name_jp: '無形固定資産の売却による収入', + unit: '百万円' + }, + purchase_of_non_current_assets: { + name_jp: '固定資産の取得による支出', + unit: '百万円' + }, + sale_of_non_current_assets: { + name_jp: '固定資産の売却による収入', + unit: '百万円' + }, + purchase_of_securities: { + name_jp: '有価証券の取得による支出', + unit: '百万円' + }, + sale_of_securities: { + name_jp: '有価証券の売却・償還による収入', + unit: '百万円' + }, + purchase_of_investment_securities: { + name_jp: '投資有価証券の取得による支出', + unit: '百万円' + }, + sale_of_investment_securities: { + name_jp: '投資有価証券の売却・償還による収入', + unit: '百万円' + }, + lending: { + name_jp: '貸付けによる支出', + unit: '百万円' + }, + return_of_lending: { + name_jp: '貸付金の回収による収入', + unit: '百万円' + }, + financial_cash_flow: { + name_jp: '財務キャッシュフロー', + unit: '百万円' + }, + net_short_term_debt: { + name_jp: '短期借入金の純増減額', + unit: '百万円' + }, + long_term_debt_issuance: { + name_jp: '長期借入れによる収入', + unit: '百万円' + }, + long_term_debt_repayment: { + name_jp: '長期借入金の返済による支出', + unit: '百万円' + }, + bonds_issuance: { + name_jp: '社債の発行による収入', + unit: '百万円' + }, + bonds_repayment: { + name_jp: '社債の償還による支出', + unit: '百万円' + }, + share_repurchase: { + name_jp: '自己株式の取得による支出', + unit: '百万円' + }, + share_sales: { + name_jp: '自己株式の売却による収入', + unit: '百万円' + }, + dividend_payment: { + name_jp: '配当金の支払額', + unit: '百万円' + }, + cash_translation_difference: { + name_jp: '現金及び現金同等物に係る換算差額', + unit: '百万円' + }, + free_cash_flow: { + name_jp: 'フリーキャッシュフロー', + unit: '百万円' + }, + ex_net_sales: { + name_jp: '売上(会社予想)', + unit: '百万円' + }, + ex_operating_income: { + name_jp: '営業利益(会社予想)', + unit: '百万円' + }, + ex_ordinary_income: { + name_jp: '経常利益(会社予想)', + unit: '百万円' + }, + ex_net_income: { + name_jp: '当期純利益(会社予想)', + unit: '百万円' + }, + dividend: { + name_jp: '配当金(実績)', + unit: '円' + }, + eps_actual: { + name_jp: 'EPS(実績)', + unit: '円' + }, + bps: { + name_jp: 'BPS', + unit: '円' + }, + ebitda_forecast: { + name_jp: 'EBITDA(会社予想)', + unit: '百万円' + }, + ebitda_actual: { + name_jp: 'EBITDA(実績)', + unit: '百万円' + }, + roe: { + name_jp: 'ROE', + unit: '%' + }, + real_roe: { + name_jp: '実質ROE', + unit: '%' + }, + total_asset_turnover: { + name_jp: '総資産回転率', + unit: '倍' + }, + financial_leverage: { + name_jp: '財務レバレッジ', + unit: '倍' + }, + roa: { + name_jp: 'ROA', + unit: '%' + }, + roic: { + name_jp: 'ROIC', + unit: '%' + }, + doe: { + name_jp: '自己資本配当率', + unit: '%' + }, + net_sales_operating_cash_flow_ratio: { + name_jp: '営業キャッシュフロー/売上比率', + unit: '%' + }, + sga_ratio: { + name_jp: '販管費/売上率', + unit: '%' + }, + depreciation_gross_profit_ratio: { + name_jp: '減価償却費/粗利比率', + unit: '%' + }, + r_and_d_ratio: { + name_jp: '研究開発費/売上比率', + unit: '%' + }, + interest_op_income_ratio: { + name_jp: '支払利息/営業利益比率', + unit: '%' + }, + interest_coverage_ratio: { + name_jp: 'インタレストカバレッジレシオ ', + unit: '倍' + }, + net_sales_progress: { + name_jp: '売上進捗率', + unit: '%' + }, + operating_income_progress: { + name_jp: '営業利益進捗率', + unit: '%' + }, + net_income_progress: { + name_jp: '純利益進捗率', + unit: '%' + }, + net_sales_growth_rate_forecast: { + name_jp: '売上高成長率(直近年度実績→会社予想)', + unit: '%' + }, + operating_income_growth_rate_forecast: { + name_jp: '営業利益成長率(直近年度実績→会社予想)', + unit: '%' + }, + net_income_growth_rate_forecast: { + name_jp: '純利益成長率(直近年度実績→会社予想)', + unit: '%' + }, + cash_assets_ratio: { + name_jp: '現金総資産比率', + unit: '%' + }, + cash_monthly_sales_ratio: { + name_jp: '現金売上倍率', + unit: '倍' + }, + accounts_receivable_turnover: { + name_jp: '売上債権回転期間', + unit: '日' + }, + inventory_turnover: { + name_jp: '棚卸資産回転期間', + unit: '日' + }, + trade_payable_turnover: { + name_jp: '仕入債務回転期間', + unit: '日' + }, + working_capital: { + name_jp: '運転資本', + unit: '百万円' + }, + ccc: { + name_jp: 'CCC', + unit: '日' + }, + tangible_fixed_assets_turnover: { + name_jp: '有形固定資産回転率', + unit: '%' + }, + debt_assets_ratio: { + name_jp: '有利子負債/総資産比率', + unit: '%' + }, + debt_monthly_sales_ratio: { + name_jp: '有利子負債/月商比率', + unit: 'ヶ月' + }, + operating_cash_flow_debt_ratio: { + name_jp: '有利子負債/営業キャッシュフロー倍率', + unit: '倍' + }, + net_debt: { + name_jp: '純有利子負債', + unit: '百万円' + }, + adjusted_debt_ratio: { + name_jp: '自己株式調整済負債比率', + unit: '%' + }, + de_ratio: { + name_jp: 'DE比率', + unit: '%' + }, + current_ratio: { + name_jp: '流動比率', + unit: '%' + }, + net_debt_net_income_ratio: { + name_jp: 'ネットD純利益比率', + unit: '倍' + }, + equity: { + name_jp: '自己資本', + unit: '百万円' + }, + equity_ratio: { + name_jp: '自己資本比率', + unit: '%' + }, + accrual: { + name_jp: 'アクルーアル ', + unit: '百万円' + }, + employee_num: { + name_jp: '従業員数', + unit: '人' + }, + net_sales_per_employee: { + name_jp: '従業員一人あたり売上', + unit: '百万円' + }, + operating_income_per_employee: { + name_jp: '従業員一人あたり営業利益', + unit: '百万円' + }, + fiscal_year: { + name_jp: '会計年度', + unit: 'なし' + }, + fiscal_quarter: { + name_jp: '四半期', + unit: 'なし' + } +} diff --git a/src/__mocks__/fixtures/v3/quarter.js b/src/__mocks__/fixtures/v3/quarter.js new file mode 100644 index 0000000..6ee4525 --- /dev/null +++ b/src/__mocks__/fixtures/v3/quarter.js @@ -0,0 +1,187 @@ +module.exports = { + 'data': { + issued_share_num: null, + treasury_stock_num: 0.0, + ex_net_sales: 52000000000.0, + ex_operating_income: 25200000000.0, + ex_ordinary_income: null, + ex_net_income: 17090000000.0, + ex_dividend: 36.0, + dividend: null, + fiscal_year: 2018.0, + fiscal_quarter: 1.0, + company_name: '株式会社カカクコム', + ceo_name: '代表取締役社長  畑 彰之介', + headquarters_address: '東京都渋谷区恵比寿南三丁目5番7号', + accounting_standard: 'IFRS', + employee_num: null, + assets: 42143000000.0, + current_assets: 28774000000.0, + cash_and_deposits: 20517000000.0, + notes_accounts_receivable: 6857000000.0, + notes_receivable: null, + accounts_receivable: null, + inventories: null, + merchandise: null, + work_in_process: null, + raw_materials_and_supplies: null, + non_current_assets: 13369000000.0, + tangible_fixed_assets: null, + liabilities: 7737000000.0, + current_liabilities: 7132000000.0, + notes_accounts_payable: 1986000000.0, + accounts_payable: null, + notes_payable: null, + short_term_bonds_payable: null, + short_term_loans_payable: 223000000.0, + commercial_papers_liabilities: null, + current_lease_obligations: null, + current_portion_of_long_term_loans: null, + current_portion_of_bonds: null, + current_portion_of_convertible_bonds: null, + current_portion_of_bonds_with_subscription_rights: null, + non_current_liabilities: 605000000.0, + bonds_payable: null, + convertible_bonds: null, + convertible_bond_type_bonds_with_subscription_rights: null, + non_current_bonds_with_subscription_right: null, + long_term_loans_payable: 264000000.0, + non_current_lease_obligations: null, + net_assets: 34406000000.0, + shareholders_equity: 34049000000.0, + capital_stock: 916000000.0, + retained_earnings: 33238000000.0, + treasury_stock: -1115000000.0, + valuation_and_translation_adjustments: null, + non_controlling_interests: 357000000.0, + subscription_rights: 172000000.0, + net_sales: 12513000000.0, + cost_of_sales: 0.0, + gross_profit: 12512000000.0, + sga: 6952000000.0, + operating_income: 5560000000.0, + non_operating_income: 3000000.0, + non_operating_expenses: 31000000.0, + interest_expense: null, + ordinary_income: 5533000000.0, + extraordinary_income: null, + extraordinary_loss: null, + impairment_loss: null, + income_before_income_taxes: 5533000000.0, + income_taxes: 1997000000.0, + net_income: 3534000000.0, + operating_cash_flow: 3110000000.0, + investment_cash_flow: -525000000.0, + financial_cash_flow: -3098000000.0, + depreciation: 452000000.0, + amortization: null + }, + column_description: { + company_name: { name_jp: '社名', unit: 'なし' }, + ceo_name: { name_jp: '代表社名', unit: 'なし' }, + headquarters_address: { name_jp: '所在地', unit: 'なし' }, + accounting_standard: { name_jp: '会計基準', unit: 'なし' }, + issued_share_num: { name_jp: '発行済株式総数', unit: '株' }, + employee_num: { name_jp: '従業員数', unit: '人' }, + assets: { name_jp: '総資産', unit: '百万円' }, + current_assets: { name_jp: '流動資産', unit: '百万円' }, + cash_and_deposits: { name_jp: '現預金', unit: '百万円' }, + notes_accounts_receivable: { + name_jp: '受取手形及び売掛金', + unit: '百万円' + }, + notes_receivable: { name_jp: '受取手形', unit: '百万円' }, + accounts_receivable: { name_jp: '売掛金', unit: '百万円' }, + inventories: { name_jp: '在庫', unit: '百万円' }, + merchandise: { name_jp: '商品', unit: '百万円' }, + work_in_process: { name_jp: '仕掛品', unit: '百万円' }, + raw_materials_and_supplies: { name_jp: '原材料', unit: '百万円' }, + non_current_assets: { name_jp: '固定資産', unit: '百万円' }, + tangible_fixed_assets: { name_jp: '有形固定資産', unit: '百万円' }, + liabilities: { name_jp: '負債', unit: '百万円' }, + current_liabilities: { name_jp: '流動負債', unit: '百万円' }, + notes_accounts_payable: { name_jp: '支払手形及び買掛金', unit: '百万円' }, + accounts_payable: { name_jp: '買掛金', unit: '百万円' }, + notes_payable: { name_jp: '支払手形', unit: '百万円' }, + short_term_bonds_payable: { name_jp: '短期社債', unit: '百万円' }, + short_term_loans_payable: { name_jp: '短期借入金', unit: '百万円' }, + commercial_papers_liabilities: { + name_jp: 'コマーシャルペーパー', + unit: '百万円' + }, + current_lease_obligations: { + name_jp: 'リース債務(流動負債)', + unit: '百万円' + }, + current_portion_of_long_term_loans: { + name_jp: '1年以内返済の長期借入金', + unit: '百万円' + }, + current_portion_of_bonds: { name_jp: '1年以内返済の社債', unit: '百万円' }, + current_portion_of_convertible_bonds: { + name_jp: '1年以内返済の転換社債', + unit: '百万円' + }, + current_portion_of_bonds_with_subscription_rights: { + name_jp: '1年以内返済の新株予約権付社債', + unit: '百万円' + }, + non_current_liabilities: { name_jp: '固定負債', unit: '百万円' }, + bonds_payable: { name_jp: '社債', unit: '百万円' }, + convertible_bonds: { name_jp: '転換社債', unit: '百万円' }, + convertible_bond_type_bonds_with_subscription_rights: { + name_jp: '新株予約権付転換社債', + unit: '百万円' + }, + non_current_bonds_with_subscription_right: { + name_jp: '新株予約権付社債', + unit: '百万円' + }, + long_term_loans_payable: { name_jp: '長期借入金', unit: '百万円' }, + non_current_lease_obligations: { + name_jp: 'リース債務(固定負債)', + unit: '百万円' + }, + net_assets: { name_jp: '純資産', unit: '百万円' }, + shareholders_equity: { name_jp: '株主資本', unit: '百万円' }, + capital_stock: { name_jp: '資本金', unit: '百万円' }, + retained_earnings: { name_jp: '利益剰余金', unit: '百万円' }, + treasury_stock: { name_jp: '自己株式', unit: '百万円' }, + valuation_and_translation_adjustments: { + name_jp: '評価換算差額等', + unit: '百万円' + }, + non_controlling_interests: { name_jp: '非支配持分', unit: '百万円' }, + subscription_rights: { name_jp: '新株予約権', unit: '百万円' }, + net_sales_summary: { name_jp: '売上', unit: '百万円' }, + net_sales: { name_jp: '売上', unit: '百万円' }, + cost_of_sales: { name_jp: '売上原価', unit: '百万円' }, + gross_profit: { name_jp: '売上高総利益', unit: '百万円' }, + sga: { name_jp: '販売費および一般管理費', unit: '百万円' }, + operating_income: { name_jp: '営業利益', unit: '百万円' }, + non_operating_income: { name_jp: '営業外利益', unit: '百万円' }, + non_operating_expenses: { name_jp: '営業外損失', unit: '百万円' }, + interest_expense: { name_jp: '支払利息', unit: '百万円' }, + ordinary_income: { name_jp: '経常利益', unit: '百万円' }, + extraordinary_income: { name_jp: '特別利益', unit: '百万円' }, + extraordinary_loss: { name_jp: '特別損失', unit: '百万円' }, + impairment_loss: { name_jp: '減損損失', unit: '百万円' }, + income_before_income_taxes: { name_jp: '税引前当期純利益', unit: '百万円' }, + income_taxes: { name_jp: '法人税等', unit: '百万円' }, + net_income: { name_jp: '当期純利益', unit: '百万円' }, + operating_cash_flow: { name_jp: '営業キャッシュフロー', unit: '百万円' }, + investment_cash_flow: { name_jp: '投資キャッシュフロー', unit: '百万円' }, + financial_cash_flow: { name_jp: '財務キャッシュフロー', unit: '百万円' }, + depreciation: { name_jp: '減価償却費', unit: '百万円' }, + amortization: { name_jp: 'のれん償却費', unit: '百万円' }, + treasury_stock_num: { name_jp: '自己株数', unit: '株' }, + ex_net_sales: { name_jp: '売上(会予)', unit: '百万円' }, + ex_operating_income: { name_jp: '営業利益(会予)', unit: '百万円' }, + ex_ordinary_income: { name_jp: '経常利益(会予)', unit: '百万円' }, + ex_net_income: { name_jp: '当期純利益(会予)', unit: '百万円' }, + ex_dividend: { name_jp: '配当予想', unit: '円' }, + dividend: { name_jp: '配当金', unit: '円' }, + fiscal_year: { name_jp: '会計年度', unit: '年' }, + fiscal_quarter: { name_jp: '四半期', unit: 'なし' } + } +} diff --git a/src/api/v3/client.test.ts b/src/api/v3/client.test.ts new file mode 100644 index 0000000..1c8e860 --- /dev/null +++ b/src/api/v3/client.test.ts @@ -0,0 +1,166 @@ +import * as company from '~/__mocks__/fixtures/v3/company' +import * as daily from '~/__mocks__/fixtures/v3/daily' +import * as quarter from '~/__mocks__/fixtures/v3/quarter' +import { HttpError } from '~/api/http-error' +import { useMockedUrlFetchApp } from '~/api/test-helper' +import { BuffettCodeApiClientV3 } from '~/api/v3/client' +import { DateParam } from '~/fiscal-periods/date-param' +import { YearQuarterParam } from '~/fiscal-periods/year-quarter-param' + +describe('BuffettCodeApiClientV3', () => { + test('HttpError#isInvalidTestingRequest', () => { + const res1 = useMockedUrlFetchApp( + 200, + '{"message":"Testing apikey is not allowed"}' + )() + const error1 = new HttpError(res1) + expect(error1.isInvalidTestingRequest()).toBeTruthy() + + const res2 = useMockedUrlFetchApp(403, '{"message": "Forbidden"}')() + const error2 = new HttpError(res2) + expect(error2.isInvalidTestingRequest()).toBeFalsy() + }) + + test('request', () => { + const mockFetch = useMockedUrlFetchApp( + 200, + '{"message": "this is a message"}' + ) + + expect( + BuffettCodeApiClientV3['request']('http://example.com', { + headers: { 'x-api-key': 'foo' } + }) + ).toEqual({ + message: 'this is a message' + }) + expect(mockFetch.mock.calls.length).toBe(1) + expect(mockFetch.mock.calls[0].length).toBe(2) + expect(mockFetch.mock.calls[0][0]).toBe('http://example.com') + expect(mockFetch.mock.calls[0][1]).toEqual({ + headers: { 'x-api-key': 'foo' }, + muteHttpExceptions: true + }) + }) + + test('request when testing apikey error', () => { + useMockedUrlFetchApp( + 200, + '{"message":"Testing Apikey is only allowed to ticker ending with \\"01\\""}' + ) + + const request = BuffettCodeApiClientV3['request'] + expect(() => request('http://example.com')).toThrow(HttpError) + }) + + test('request when 403 error', () => { + useMockedUrlFetchApp(403, '{"message": "Forbidden"}') + + const request = BuffettCodeApiClientV3['request'] + expect(() => request('http://example.com')).toThrow(HttpError) + }) + + test('request when 503 error', () => { + useMockedUrlFetchApp(503, '{"message": "Service Unavailable"}') + + const request = BuffettCodeApiClientV3['request'] + expect(() => request('http://example.com')).toThrow(HttpError) + try { + request('http://example.com') + } catch (e) { + expect(e.response.getResponseCode()).toBe(503) + expect(e.response.getContentText()).toBe( + '{"message": "Service Unavailable"}' + ) + } + }) + + test('company', () => { + const mockFetch = useMockedUrlFetchApp(200, JSON.stringify(company)) + + const client = new BuffettCodeApiClientV3('foo') + const ticker = '2371' + expect(client.company(ticker)).toEqual(company[ticker]) + expect(mockFetch.mock.calls.length).toBe(1) + expect(mockFetch.mock.calls[0].length).toBe(2) + expect(mockFetch.mock.calls[0][0]).toBe( + `https://api.buffett-code.com/api/v3/company?ticker=${ticker}` + ) + expect(mockFetch.mock.calls[0][1]).toEqual({ + headers: { 'x-api-key': 'foo' }, + muteHttpExceptions: true + }) + }) + + test('quarter', () => { + const mockFetch = useMockedUrlFetchApp(200, JSON.stringify(quarter)) + + const client = new BuffettCodeApiClientV3('foo') + const ticker = '2371' + const period = new YearQuarterParam(2018, 1) + expect(client.quarter(ticker, period)).toEqual(quarter['data']) + expect(mockFetch.mock.calls.length).toBe(1) + expect(mockFetch.mock.calls[0].length).toBe(2) + expect(mockFetch.mock.calls[0][0]).toBe( + 'https://api.buffett-code.com/api/v3/quarter?ticker=2371&fy=2018&fq=1' + ) + expect(mockFetch.mock.calls[0][1]).toEqual({ + headers: { 'x-api-key': 'foo' }, + muteHttpExceptions: true + }) + }) + + test('ondemandQuarter', () => { + const mockFetch = useMockedUrlFetchApp(200, JSON.stringify(quarter)) + + const client = new BuffettCodeApiClientV3('foo') + const ticker = '2371' + const period = new YearQuarterParam(2018, 1) + expect(client.ondemandQuarter(ticker, period)).toEqual(quarter['data']) + expect(mockFetch.mock.calls.length).toBe(1) + expect(mockFetch.mock.calls[0].length).toBe(2) + expect(mockFetch.mock.calls[0][0]).toBe( + 'https://api.buffett-code.com/api/v3/ondemand/quarter?ticker=2371&fy=2018&fq=1' + ) + expect(mockFetch.mock.calls[0][1]).toEqual({ + headers: { 'x-api-key': 'foo' }, + muteHttpExceptions: true + }) + }) + + test('daily', () => { + const mockFetch = useMockedUrlFetchApp(200, JSON.stringify(daily)) + + const client = new BuffettCodeApiClientV3('foo') + const ticker = '2371' + const date = new DateParam(new Date('2021-08-11')) + expect(client.daily(ticker, date)).toEqual(daily['data']) + expect(mockFetch.mock.calls.length).toBe(1) + expect(mockFetch.mock.calls[0].length).toBe(2) + expect(mockFetch.mock.calls[0][0]).toBe( + 'https://api.buffett-code.com/api/v3/daily?ticker=2371&date=2021-08-11' + ) + expect(mockFetch.mock.calls[0][1]).toEqual({ + headers: { 'x-api-key': 'foo' }, + muteHttpExceptions: true + }) + }) + + test('ondemandDaily', () => { + const mockFetch = useMockedUrlFetchApp(200, JSON.stringify(daily)) + + const client = new BuffettCodeApiClientV3('foo') + const ticker = '2371' + const date = new DateParam(new Date('2021-08-11')) + expect(client.ondemandDaily(ticker, date)).toEqual(daily['data']) + expect(mockFetch.mock.calls.length).toBe(1) + expect(mockFetch.mock.calls[0].length).toBe(2) + expect(mockFetch.mock.calls[0][0]).toBe( + 'https://api.buffett-code.com/api/v3/ondemand/daily?ticker=2371&date=2021-08-11' + ) + expect(mockFetch.mock.calls[0][1]).toEqual({ + headers: { 'x-api-key': 'foo' }, + muteHttpExceptions: true + }) + }) +}) diff --git a/src/api/v3/client.ts b/src/api/v3/client.ts new file mode 100644 index 0000000..03530e6 --- /dev/null +++ b/src/api/v3/client.ts @@ -0,0 +1,111 @@ +import { HttpError } from '~/api/http-error' +import { UrlBuilder } from '~/api/url-builder' +import { DateParam } from '~/fiscal-periods/date-param' +import { YearQuarterParam } from '~/fiscal-periods/year-quarter-param' + +export class BuffettCodeApiClientV3 { + static readonly baseUrl = 'https://api.buffett-code.com/api/v3' + + constructor(private _token: string) {} + + private static request(url: string, options = {}): object { + const defaultOptions = { + muteHttpExceptions: true + } + const fullOptions = { ...defaultOptions, ...options } + const res = UrlFetchApp.fetch(url, fullOptions) + + const code = res.getResponseCode() + const content = res.getContentText() + const error = new HttpError(res) + if ( + Math.floor(code / 100) === 4 || + Math.floor(code / 100) === 5 || + error.isInvalidTestingRequest() + ) { + throw error + } + + let json + try { + json = JSON.parse(content) + } catch (e) { + console.error('JSON parsing error', code, content) + throw new HttpError(res) + } + + return json + } + + private defaultOptions(): object { + return { + headers: { + 'x-api-key': this._token + } + } + } + + public company(ticker: string): object { + const endpoint = BuffettCodeApiClientV3.baseUrl + '/company' + const builder = new UrlBuilder(endpoint, { ticker: ticker }) + const url = builder.toString() + const options = this.defaultOptions() + + const res = BuffettCodeApiClientV3.request(url, options) + return res[ticker] + } + + public quarter(ticker: string, period: YearQuarterParam): object { + const endpoint = BuffettCodeApiClientV3.baseUrl + '/quarter' + const builder = new UrlBuilder(endpoint, { + ticker, + fy: period.year, + fq: period.quarter + }) + const url = builder.toString() + const options = this.defaultOptions() + + const res = BuffettCodeApiClientV3.request(url, options) + return res['data'] + } + + public ondemandQuarter(ticker: string, period: YearQuarterParam): object { + const endpoint = BuffettCodeApiClientV3.baseUrl + '/ondemand/quarter' + const builder = new UrlBuilder(endpoint, { + ticker, + fy: period.year, + fq: period.quarter + }) + const url = builder.toString() + const options = this.defaultOptions() + + const res = BuffettCodeApiClientV3.request(url, options) + return res['data'] + } + + public daily(ticker: string, date: DateParam): object { + const endpoint = BuffettCodeApiClientV3.baseUrl + '/daily' + const builder = new UrlBuilder(endpoint, { + ticker, + date: date.toString() + }) + const url = builder.toString() + const options = this.defaultOptions() + + const res = BuffettCodeApiClientV3.request(url, options) + return res['data'] + } + + public ondemandDaily(ticker: string, date: DateParam): object { + const endpoint = BuffettCodeApiClientV3.baseUrl + '/ondemand/daily' + const builder = new UrlBuilder(endpoint, { + ticker, + date: date.toString() + }) + const url = builder.toString() + const options = this.defaultOptions() + + const res = BuffettCodeApiClientV3.request(url, options) + return res['data'] + } +} diff --git a/src/fiscal-periods/date-param.test.ts b/src/fiscal-periods/date-param.test.ts new file mode 100644 index 0000000..127d613 --- /dev/null +++ b/src/fiscal-periods/date-param.test.ts @@ -0,0 +1,18 @@ +import { DateParam } from '~/fiscal-periods/date-param' + +test('toString', () => { + expect(new DateParam('latest').toString()).toEqual('latest') + expect(new DateParam(new Date('2020-09-06')).toString()).toEqual('2020-09-06') +}) + +test('toDate', () => { + expect(() => new DateParam('latest').toDate()).toThrow(Error) + expect(new DateParam(new Date('2020-09-06')).toDate()).toEqual( + new Date('2020-09-06') + ) +}) + +test('isLatest', () => { + expect(new DateParam('latest').isLatest()).toBeTruthy() + expect(new DateParam(new Date('2020-09-06')).isLatest()).toBeFalsy() +}) diff --git a/src/fiscal-periods/date-param.ts b/src/fiscal-periods/date-param.ts new file mode 100644 index 0000000..55138c8 --- /dev/null +++ b/src/fiscal-periods/date-param.ts @@ -0,0 +1,23 @@ +export class DateParam { + constructor(public date: Date | 'latest') {} + + public toString(): string { + if (this.date instanceof Date) { + return this.date.toISOString().substring(0, 10) + } else { + return this.date + } + } + + public toDate(): Date { + if (this.date === 'latest') { + throw new Error('This cannot convert to Date') + } else { + return this.date + } + } + + public isLatest(): boolean { + return this.date === 'latest' + } +}