#
tokens: 6883/50000 16/16 files
lines: off (toggle) GitHub
raw markdown copy
# Directory Structure

```
├── .editorconfig
├── .gitignore
├── .prettierrc
├── .vscode
│   └── mcp.json
├── eslint.config.mjs
├── glama.json
├── LICENSE
├── package-lock.json
├── package.json
├── README.md
├── src
│   ├── application
│   │   └── services
│   │       └── StatusInvestService.ts
│   ├── domain
│   │   └── models
│   │       ├── StatusInvestApiServiceModel.ts
│   │       ├── StatusInvestModel.ts
│   │       └── StatusInvestServiceModel.ts
│   ├── infrastructure
│   │   └── services
│   │       └── StatusInvestApiService.ts
│   ├── interface
│   │   └── controllers
│   │       └── StatusInvestToolsController.ts
│   └── main.ts
└── tsconfig.json
```

# Files

--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------

```
.history/
node_modules/
build/
```

--------------------------------------------------------------------------------
/.prettierrc:
--------------------------------------------------------------------------------

```
{
  "singleQuote": true,
  "trailingComma": "all"
}

```

--------------------------------------------------------------------------------
/.editorconfig:
--------------------------------------------------------------------------------

```
root = true

[*]
indent_style = space
indent_size = 2
end_of_line = lf
charset = utf-8
trim_trailing_whitespace = true
insert_final_newline = true

```

--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------

```markdown
<div align="center">

# Status Invest - MCP Server

This project is part of the Model Context Protocol (MCP) ecosystem and provides tools to interact with external APIs and manage specific domain models. It is designed to demonstrate how to build an MCP server with external API integration and data validation.

</div>

<table style="border-collapse: collapse; width: 100%; table-layout: fixed;">
<tr>
<td style="width: 40%; padding: 15px; vertical-align: middle; border: none;">An integration that enables MCP tools to query stock market data, such as stock prices, indicators, and payment dates, using the Status Invest API.</td>
<td style="width: 60%; padding: 0; vertical-align: middle; border: none; min-width: 300px; text-align: center;"><a href="https://glama.ai/mcp/servers/@newerton/mcp-status-invest">
  <img style="max-width: 100%; height: auto; min-width: 300px;" src="https://glama.ai/mcp/servers/@newerton/mcp-status-invest/badge" alt="Status Invest - MCP Server" />
</a></td>
</tr>
</table>

## Table of Contents

- [Features](#features)
- [Architecture](#architecture)
- [Installation](#installation)
- [MCP Server Configuration in VSCode](#mcp-server-configuration-in-vscode)
- [MCP Server Output in VSCode](#mcp-server-output-in-vscode)
- [Contribution](#contribution)
- [License](#license)

## Features

- **get-acoes**: Fetch basic stock information.
- **get-indicadores**: Fetch detailed stock indicator information.
- **get-acoes-datas-pagamento**: Fetch stock payment dates.
- Input validation using [Zod](https://github.com/colinhacks/zod).
- Integration with the Status Invest API using `fetch` (infrastructure layer).

## Architecture

The project follows a layered architecture inspired by **Domain-Driven Design** (DDD) patterns:

- **Domain** (`src/domain`):
  Defines interfaces and types that represent data structures (e.g., `StatusInvest`).

- **Infrastructure** (`src/infrastructure`):
  Implements external services, such as `StatusInvestApiService`, responsible for making HTTP calls to the Status Invest API.

- **Application** (`src/application`):
  Contains business logic in `StatusInvestService`, which processes and formats data from the infrastructure.

- **Interface** (`src/interface`):
  Includes controllers (`StatusInvestToolsController`) that register tools in the MCP server, define validation schemas, and return results.

- **Entry Point** (`src/main.ts`):
  Initializes the `McpServer`, configures the transport (`StdioServerTransport`), instantiates services and controllers, and starts listening on _stdio_.

The folder structure is as follows:
```
src/
├── domain/
│   └── models/           # Domain interfaces
├── infrastructure/
│   └── services/         # External API implementations (Status Invest)
├── application/
│   └── services/         # Business logic and data formatting
├── interface/
│   └── controllers/      # MCP tool registration and validation
└── main.ts               # Server entry point
build/                    # Compiled JavaScript code
.vscode/                  # Contains the mcp.json file, MCP Server config
```

## Installation

```bash
git clone [email protected]:newerton/mcp-status-invest.git
cd mcp-status-invest
npm install
npm run build
```

## MCP Server Configuration in VSCode

1. Press `Ctrl+Shift+P` and select "MCP: List Servers"
2. Select "stocks" and then "Start Server"

## MCP Server Output in VSCode

1. Press `Ctrl+Shift+P` and select "MCP: List Servers"
2. Select "stocks" and then "Show Output"

## Contribution

Pull requests are welcome! Feel free to open issues and discuss improvements.

## License

This project is licensed under the MIT license - see the [LICENSE](https://github.com/imprvhub/mcp-claude-hackernews/blob/main/LICENSE) file for details.


```

--------------------------------------------------------------------------------
/src/domain/models/StatusInvestApiServiceModel.ts:
--------------------------------------------------------------------------------

```typescript
export interface GetPaymentDatesInput {
  initialDate: string;
  finalDate: string;
  stock?: string;
}

```

--------------------------------------------------------------------------------
/glama.json:
--------------------------------------------------------------------------------

```json
{
  "$schema": "https://glama.ai/mcp/schemas/server.json",
  "maintainers": [
    "Newerton Vargas de Araujo"
  ]
}

```

--------------------------------------------------------------------------------
/.vscode/mcp.json:
--------------------------------------------------------------------------------

```json
{
  "servers": {
    "stocks": {
      "type": "stdio",
      "command": "node",
      "args": [
        "D:/www/newerton/mcp/mcp-status-invest/build/main.js"
      ]
    }
  }
}

```

--------------------------------------------------------------------------------
/src/domain/models/StatusInvestServiceModel.ts:
--------------------------------------------------------------------------------

```typescript
export interface GetPaymentDatesInput {
  initialDate: string;
  finalDate: string;
  stocks?: string[];
}

export interface GetPaymentDatesOutput {
  code: string;
  companyName: string;
  price: number;
  dateCom: string;
  paymentDate: string;
  type: string;
  dy: string;
  url: string;
}

```

--------------------------------------------------------------------------------
/tsconfig.json:
--------------------------------------------------------------------------------

```json
{
  "compilerOptions": {
    "target": "ES2024",
    "module": "NodeNext",
    "moduleResolution": "NodeNext",
    "outDir": "./build",
    "rootDir": "./src",
    "strict": true,
    "esModuleInterop": true,
    "skipLibCheck": true,
    "forceConsistentCasingInFileNames": true
  },
  "include": ["src/**/*"],
  "exclude": ["node_modules"]
}
```

--------------------------------------------------------------------------------
/src/main.ts:
--------------------------------------------------------------------------------

```typescript
import { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js';

import { StatusInvestService } from './application/services/StatusInvestService.js';
import { StatusInvestApiService } from './infrastructure/services/StatusInvestApiService.js';
import { StatusInvestToolsController } from './interface/controllers/StatusInvestToolsController.js';

async function main() {
  const server = new McpServer({
    name: 'stocks',
    version: '1.0.0',
    capabilities: {
      resources: {},
      tools: {},
    },
  });

  const apiService = new StatusInvestApiService();
  const service = new StatusInvestService(apiService);

  new StatusInvestToolsController(server, service);

  const transport = new StdioServerTransport();
  await server.connect(transport);
  console.error('Status Invest MCP Server running on stdio');
}

main().catch((error) => {
  console.error('Fatal error in main():', error);
  process.exit(1);
});

```

--------------------------------------------------------------------------------
/src/domain/models/StatusInvestModel.ts:
--------------------------------------------------------------------------------

```typescript
export enum TypeEnum {
  'ação' = 1,
  'fii' = 2,
  'bdr' = 4,
  'etf' = 6,
  'stock' = 12,
  'fundos' = 15,
  'fii-infra' = 22,
  'fiagro' = 24,
  'cripto' = 100,
  'etf-exterior' = 901,
}

export interface MainSearchQuery {
  id: number;
  parentId: number;
  nameFormated: string;
  name: string;
  normalizedName: string;
  code: string;
  price: string;
  variation: string;
  variationUp: boolean;
  type: TypeEnum;
  url: string;
}

export interface GetEarnings {
  category: number;
  from: string;
  controller: string;
  close: boolean;
  dateCom: GetEarningsDateItem[];
  datePayment: GetEarningsDateItem[];
  provisioned: string[];
}

export interface GetEarningsDateItem {
  code: string;
  companyName: string;
  companyNameClean: string;
  companyId: number;
  resultAbsoluteValue: string;
  dateCom: string;
  paymentDividend: string;
  earningType: string;
  dy: string;
  recentEvents: number;
  recentReports: number;
  uRLClear: string;
  rankDateCom: number;
  rankPaymentDividend: number;
}

```

--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------

```json
{
  "name": "mcp-status-invest",
  "version": "1.1.3",
  "description": "MCP to Status Invest Scraper",
  "main": "build/main.js",
  "repository": {
    "type": "git",
    "url": "[email protected]:newerton/mcp-status-invest.git"
  },
  "author": "Newerton (https://newerton.com)",
  "type": "module",
  "license": "MIT",
  "bin": "./build/index.js",
  "files": [
    "build"
  ],
  "keywords": [
    "mpc",
    "status invest"
  ],
  "scripts": {
    "start": "node build/main.js",
    "start:dev": "tsx watch src/main.ts",
    "build": "tsc && chmod 755 build/main.js",
    "lint": "eslint \"{src,test}/**/*.ts\" --fix"
  },
  "dependencies": {
    "@modelcontextprotocol/sdk": "^1.18.0",
    "cheerio": "^1.1.2",
    "dayjs": "^1.11.18",
    "voca": "^1.4.1",
    "zod": "^3.23.8"
  },
  "devDependencies": {
    "@types/node": "^24.5.1",
    "@types/voca": "^1.4.6",
    "@typescript-eslint/eslint-plugin": "^8.44.0",
    "@typescript-eslint/parser": "^8.44.0",
    "eslint": "^9.35.0",
    "eslint-config-prettier": "^10.1.8",
    "eslint-import-resolver-typescript": "^4.4.4",
    "eslint-plugin-import": "^2.32.0",
    "eslint-plugin-prettier": "^5.5.4",
    "eslint-plugin-unused-imports": "^4.2.0",
    "prettier": "^3.6.2",
    "ts-node": "^10.9.2",
    "tsx": "^4.20.5",
    "typescript": "^5.9.2",
    "typescript-eslint": "^8.44.0"
  }
}

```

--------------------------------------------------------------------------------
/eslint.config.mjs:
--------------------------------------------------------------------------------

```
import importPlugin from 'eslint-plugin-import';
import eslint from '@eslint/js';
import eslintPluginPrettierRecommended from 'eslint-plugin-prettier/recommended';
import globals from 'globals';
import tseslint from 'typescript-eslint';
import unusedImports from "eslint-plugin-unused-imports";

export default tseslint.config(
  {
    ignores: ['eslint.config.mjs', 'pm2.config.js'],
  },
  {
    plugins: {
      "unused-imports": unusedImports,
    }
  },
  eslint.configs.recommended,
  ...tseslint.configs.recommendedTypeChecked,
  eslintPluginPrettierRecommended,
  importPlugin.flatConfigs.recommended,
  {
    files: ['**/*.{ts,tsx}'],
    extends: [importPlugin.flatConfigs.recommended, importPlugin.flatConfigs.typescript],
  },

  {
    languageOptions: {
      globals: {
        ...globals.node,
        ...globals.jest,
      },
      ecmaVersion: 5,
      sourceType: 'module',
      parserOptions: {
        projectService: true,
        tsconfigRootDir: import.meta.dirname,
      },
    },
  },
  {
    rules: {
      '@typescript-eslint/no-explicit-any': 'off',
      '@typescript-eslint/no-floating-promises': 'warn',
      '@typescript-eslint/no-unsafe-argument': 'warn',
      '@typescript-eslint/no-unsafe-assignment': 'off',
      '@typescript-eslint/no-unsafe-member-access': 'off',
      '@typescript-eslint/no-unsafe-call': 'off',

      "sort-imports": ["error", {
        ignoreCase: false,
        ignoreDeclarationSort: true,
        ignoreMemberSort: false,
        memberSyntaxSortOrder: ["none", "all", "multiple", "single"],
        allowSeparatedGroups: true,
      }],

      "import/no-unresolved": "error",
      "unused-imports/no-unused-imports": "error",

      "import/order": ["error", {
        groups: [
          "builtin",
          "external",
          "internal",
          ["sibling", "parent"],
          "index",
          "unknown",
        ],

        "newlines-between": "always",

        alphabetize: {
          order: "asc",
          caseInsensitive: true,
        },
      }],

    },

    settings: {
      "import/resolver": {
        typescript: {
          project: import.meta.dirname + '/tsconfig.json',
        },
      },
    },
  },
);

```

--------------------------------------------------------------------------------
/src/infrastructure/services/StatusInvestApiService.ts:
--------------------------------------------------------------------------------

```typescript
import { GetPaymentDatesInput } from '../../domain/models/StatusInvestApiServiceModel.js';
import {
  GetEarnings,
  MainSearchQuery,
} from '../../domain/models/StatusInvestModel.js';

export class StatusInvestApiService {
  private readonly API_BASE = 'https://statusinvest.com.br';
  private readonly USER_AGENT =
    'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/135.0.0.0 Safari/537.36';

  // Helper function for making NWS API requests
  async makeJsonRequest<T>(endpoint: string): Promise<T | null> {
    const url = `${this.API_BASE}${endpoint}`;
    const headers = {
      'User-Agent': this.USER_AGENT,
    };

    try {
      const response = await fetch(url, { headers });
      if (!response.ok) {
        throw new Error(`HTTP error! status: ${response.status}`);
      }
      return (await response.json()) as T;
    } catch (error) {
      console.error('Error making NWS request:', error);
      return null;
    }
  }

  async makeTextRequest<T>(endpoint: string): Promise<T | null> {
    const url = `${this.API_BASE}${endpoint}`;
    const headers = {
      'User-Agent': this.USER_AGENT,
    };

    try {
      const response = await fetch(url, { headers });
      if (!response.ok) {
        throw new Error(`HTTP error! status: ${response.status}`);
      }
      return (await response.text()) as T;
    } catch (error) {
      console.error('Error making NWS request:', error);
      return null;
    }
  }

  getUrlBase(): string {
    return this.API_BASE;
  }

  async getStockResume(stock: string): Promise<MainSearchQuery[] | null> {
    const data = await this.makeJsonRequest<MainSearchQuery[]>(
      `/home/mainsearchquery?q=${stock.toLowerCase()}`,
    );
    if (!data) return null;
    return data;
  }

  async getIndicators(stock: string): Promise<string | null> {
    const data = await this.makeTextRequest<string>(
      `/acoes/${stock.toLowerCase()}`,
    );
    if (!data) return null;
    return data;
  }

  async getStockPaymentDates(
    paymentDatesInput: GetPaymentDatesInput,
  ): Promise<GetEarnings | null> {
    const { initialDate, finalDate, stock } = paymentDatesInput;
    let url = `/acao/getearnings?IndiceCode=Ibovespa&Start=${initialDate}&End=${finalDate}`;
    if (stock) {
      url += `&Filter=${stock}`;
    }
    const data = await this.makeJsonRequest<GetEarnings>(url);
    if (!data) return null;
    return data;
  }
}

```

--------------------------------------------------------------------------------
/src/interface/controllers/StatusInvestToolsController.ts:
--------------------------------------------------------------------------------

```typescript
import { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
import dayjs from 'dayjs';
import { z } from 'zod/v3';

import { StatusInvestService } from '../../application/services/StatusInvestService.js';
import { GetPaymentDatesInput } from '../../domain/models/StatusInvestServiceModel.js';

export class StatusInvestToolsController {
  constructor(
    private server: McpServer,
    private service: StatusInvestService,
  ) {
    this.registerTools();
  }

  private registerTools() {
    this.registerGetStockToolHandler();
    this.registerGetIndicatorsToolHandler();
    this.registerGetStockPaymentDatesToolHandler();
    this.registerPortfolioAnalysisToolHandler();
  }

  private registerGetStockToolHandler(): void {
    this.server.tool(
      'get-acoes',
      'Buscar informações básicas de ações',
      {
        stocks: z.array(z.string()).describe('Array of stock symbols'),
      },
      async (args) => {
        const stocks: string[] = Array.isArray(args.stocks)
          ? args.stocks
          : [args.stocks];

        const infos = await this.service.getStockResume(stocks);

        return {
          content: [
            {
              type: 'text',
              text: JSON.stringify(infos, null, 2),
            },
          ],
        };
      },
    );
  }

  private registerGetIndicatorsToolHandler(): void {
    this.server.tool(
      'get-indicadores',
      'Buscar informações de indicadores de ações',
      {
        stocks: z.array(z.string()).describe('Array of stock symbols'),
      },
      async (args) => {
        const stocks: string[] = Array.isArray(args.stocks)
          ? args.stocks
          : [args.stocks];

        const infos = await this.service.getStockIndicators(stocks);

        return {
          content: [
            {
              type: 'text',
              text: JSON.stringify(infos, null, 2),
            },
          ],
        };
      },
    );
  }

  private registerGetStockPaymentDatesToolHandler(): void {
    this.server.tool(
      'get-acoes-datas-pagamento',
      'Buscar datas de pagamento de ações',
      {
        initialDate: z
          .string()
          .refine((date) => dayjs(date, 'YYYY-MM-DD', true).isValid(), {
            message: 'Data inicial inválida. Formato esperado: YYYY-MM-DD',
          })
          .describe('Data inicial'),
        finalDate: z
          .string()
          .refine((date) => dayjs(date, 'YYYY-MM-DD', true).isValid(), {
            message: 'Data final inválida. Formato esperado: YYYY-MM-DD',
          })
          .describe('Data final'),
        stocks: z
          .array(
            z.string().regex(/^[A-Z]{4}(3|4|11)$/, {
              message:
                'Código de ação inválido. Deve seguir o padrão: 4 letras + 3, 4 ou 11.',
            }),
          )
          .optional()
          .describe('Ação'),
      },
      async (args) => {
        const paymentDatesInput = args as GetPaymentDatesInput;
        const infos =
          await this.service.getStockPaymentDates(paymentDatesInput);

        return {
          content: [
            {
              type: 'text',
              text: JSON.stringify(infos, null, 2),
            },
          ],
        };
      },
    );
  }

  private registerPortfolioAnalysisToolHandler(): void {
    this.server.tool(
      'analise-carteira',
      'Análise completa de carteira com rebalanceamento multifatorial',
      {
        stocks: z
          .array(z.string())
          .describe('Array of stock symbols (e.g., ["BBAS3", "ITUB3"])'),
        totalAmount: z
          .number()
          .describe('Total amount available to invest in BRL'),
        orderCost: z.number().describe('Cost per order in BRL'),
        strategy: z
          .string()
          .optional()
          .describe(
            'Investment strategy (buy-and-hold, dividend-focused, etc.)',
          ),
      },
      async (args) => {
        try {
          const {
            stocks,
            totalAmount,
            orderCost,
            strategy = 'buy-and-hold',
          } = args;

          const stocksArray: string[] = Array.isArray(stocks)
            ? stocks
            : typeof stocks === 'string'
              ? [stocks]
              : [];

          const [basicInfo, indicators] = await Promise.all([
            this.service.getStockResume(stocksArray),
            this.service.getStockIndicators(stocksArray),
          ]);

          const portfolioData = {
            strategy,
            totalAmount,
            orderCost,
            stocks: stocks.map((ticker: string, index: number) => {
              const stock = indicators[index];
              const basic = basicInfo[index];
              return {
                ticker: stock.stock,
                name: basic.name,
                price: basic.price,
                data: stock,
              };
            }),
            basicInfo,
            indicators,
            timestamp: new Date().toISOString(),
          };

          return {
            content: [
              {
                type: 'text',
                text: JSON.stringify(portfolioData, null, 2),
              },
            ],
          };
        } catch (error) {
          console.error('Portfolio analysis error:', error);
          return {
            content: [
              {
                type: 'text',
                text: JSON.stringify(
                  {
                    error:
                      error instanceof Error
                        ? error.message
                        : 'Unknown error occurred during portfolio analysis',
                    timestamp: new Date().toISOString(),
                  },
                  null,
                  2,
                ),
              },
            ],
          };
        }
      },
    );
  }
}

```

--------------------------------------------------------------------------------
/src/application/services/StatusInvestService.ts:
--------------------------------------------------------------------------------

```typescript
import * as cheerio from 'cheerio';
import dayjs from 'dayjs';
import customParseFormat from 'dayjs/plugin/customParseFormat.js';
import voca from 'voca';

import { TypeEnum } from '../../domain/models/StatusInvestModel.js';
import {
  GetPaymentDatesInput,
  GetPaymentDatesOutput,
} from '../../domain/models/StatusInvestServiceModel.js';
import { StatusInvestApiService } from '../../infrastructure/services/StatusInvestApiService.js';

dayjs.extend(customParseFormat);
export class StatusInvestService {
  constructor(private apiService: StatusInvestApiService) {}

  async getStockResume(stocks: string[]) {
    const data = [];
    for (const stock of stocks) {
      const stockData = await this.apiService.getStockResume(stock);
      if (stockData && stockData?.length > 0) {
        for (const item of stockData) {
          let type = TypeEnum[item.type];
          if (!type) {
            type = `${item.type} unknown`;
          }
          const jsonData = {
            id: item.id,
            type,
            code: item.code,
            name: item.name,
            price: item.price,
            variation: item.variation,
            variationUp: item.variationUp,
            url: item.url,
            image: `https://statusinvest.com.br/img/company/avatar/${item.parentId}.jpg?v=214`,
          };
          data.push(jsonData);
        }
      }
    }
    return data;
  }

  async getStockIndicators(stocks: string[]) {
    const baseUrl = this.apiService.getUrlBase();
    const stockData = [];
    for (const stock of stocks) {
      const response = await this.apiService.getIndicators(stock);
      if (!response) continue;

      const resume = this.getResume(response);
      const indicators = this.getAllIndicators(response);

      const data = {
        stock: stock,
        url: `${baseUrl}/acoes/${stock.toLowerCase()}`,
        resume: {
          price: {
            value: resume.price.value,
            variation: resume.price.variation,
          },
          min52weeks: {
            value: resume.min52Weeks.value,
          },
          max52weeks: {
            value: resume.max52Weeks.value,
          },
          minMonth: {
            value: resume.minMonth.value,
          },
          maxMonth: {
            value: resume.maxMonth.value,
          },
          valuation12Months: {
            value: resume.valuation12Months.value,
          },
          valuationCurrentMonth: {
            value: resume.valuationCurrentMonth.value,
          },
        },
        indicators,
      };

      stockData.push(data);
    }

    return stockData;
  }

  async getStockPaymentDates(paymentDatesInput: GetPaymentDatesInput) {
    const { initialDate, finalDate, stocks } = paymentDatesInput;

    if (!stocks || stocks.length === 0) {
      const response = await this.getDatesPayment(initialDate, finalDate);
      return response;
    }

    const data = [];
    for (const stock of stocks) {
      const response = await this.getDatesPayment(
        initialDate,
        finalDate,
        stock.toUpperCase(),
      );
      if (response && response.length > 0) {
        data.push(...response);
      }
    }

    return data;
  }

  private getResume(html: string) {
    const $ = cheerio.load(html);

    const priceText = $('div[title="Valor atual do ativo"] strong.value')
      .text()
      .trim();
    const price = parseFloat(
      priceText.replace('R$', '').replace('.', '').replace(',', '.'),
    );

    const variationText = $(
      'span[title="Variação do valor do ativo com base no dia anterior"] b',
    )
      .text()
      .trim();

    const variation = parseFloat(
      variationText.replace('%', '').replace(',', '.'),
    );

    const min52weeksText = $(
      'div[title="Valor mínimo das últimas 52 semanas"] strong.value',
    )
      .text()
      .trim();

    const min52weeks = parseFloat(
      min52weeksText.replace('R$', '').replace('.', '').replace(',', '.'),
    );

    const max52weeksText = $(
      'div[title="Valor máximo das últimas 52 semanas"] strong.value',
    )
      .text()
      .trim();

    const max52weeks = parseFloat(
      max52weeksText.replace('R$', '').replace('.', '').replace(',', '.'),
    );

    const minMonthText = $(
      'div[title="Valor mínimo do mês atual"] span.sub-value',
    )
      .text()
      .trim();
    const minMonth = parseFloat(
      minMonthText.replace('R$', '').replace('.', '').replace(',', '.'),
    );
    const maxMonthText = $(
      'div[title="Valor máximo do mês atual"] span.sub-value',
    )
      .text()
      .trim();
    const maxMonth = parseFloat(
      maxMonthText.replace('R$', '').replace('.', '').replace(',', '.'),
    );

    const valuation12MonthsText = $(
      'div[title="Valorização no preço do ativo com base nos últimos 12 meses"] strong.value',
    )
      .text()
      .trim();
    const valuation12Months = parseFloat(
      valuation12MonthsText
        .replace('R$', '')
        .replace('.', '')
        .replace(',', '.'),
    );

    const valuationCurrentMonthText = $(
      'div[title="Valorização no preço do ativo com base no mês atual"] span.sub-value b',
    )
      .text()
      .trim();
    const valuationCurrentMonth = parseFloat(
      valuationCurrentMonthText.replace('%', '').replace(',', '.'),
    );

    return {
      price: {
        value: price,
        variation,
      },
      min52Weeks: {
        value: min52weeks,
      },
      max52Weeks: {
        value: max52weeks,
      },
      minMonth: {
        value: minMonth,
      },
      maxMonth: {
        value: maxMonth,
      },
      valuation12Months: {
        value: valuation12Months,
      },
      valuationCurrentMonth: {
        value: valuationCurrentMonth,
      },
    };
  }

  private getAllIndicators(html: string) {
    const $ = cheerio.load(html);

    const indicatorContainer = $(
      'div.indicator-today-container > div > div.indicators',
    );

    if (indicatorContainer.length === 0) {
      return [];
    }

    if (indicatorContainer.length > 0) {
      const valuationIndicators = indicatorContainer.map((_, element) => {
        const title = $(element).find('strong.uppercase').text().trim();

        const indicatorsSection = $(element).find('.item');

        const values = indicatorsSection
          .map((_, item) => {
            const title = $(item).find('.title').text().trim();
            const value = $(item).find('.value').text().trim();
            return {
              title,
              value: parseFloat(value.replace(',', '.')),
            };
          })
          .get();

        return {
          title: voca.camelCase(title),
          values,
        };
      });

      return valuationIndicators.toArray();
    }
  }

  private async getDatesPayment(
    initialDate: string,
    finalDate: string,
    stock?: string,
  ): Promise<GetPaymentDatesOutput[]> {
    const baseUrl = this.apiService.getUrlBase();

    const paymentDatesInput = {
      initialDate,
      finalDate,
      stock,
    };

    const paymentDate =
      await this.apiService.getStockPaymentDates(paymentDatesInput);
    const datePayments = paymentDate?.datePayment;
    if (!datePayments || datePayments.length === 0) {
      return [];
    }

    const data = datePayments.map((item) => {
      return {
        code: item.code,
        companyName: item.companyName,
        price: parseFloat(item.resultAbsoluteValue.replace(',', '.')),
        dateCom: dayjs(item.dateCom, 'DD/MM/YYYY').format('YYYY-MM-DD'),
        paymentDate: dayjs(item.paymentDividend, 'DD/MM/YYYY').format(
          'YYYY-MM-DD',
        ),
        type: item.earningType,
        dy: item.dy,
        url: `${baseUrl}${item.uRLClear}`,
      };
    });

    return data;
  }
}

```