Skill: alexander-panov/finam (version 1.0.4)

--- name: finam description: Execute trades, manage portfolios, access real-time market data, browse and search market assets, scan volatility, and answer questions about Finam Trade API metadata: '{"openclaw": {"emoji": "📈", "homepage": "https://tradeapi.finam.ru/", "requires": {"bins": ["curl", "jq", "python3"], "env": ["FINAM_API_KEY", "FINAM_ACCOUNT_ID"]}}}' --- # Finam Trade API Skill ## Setup **Prerequisites:** `$FINAM_API_KEY` and `$FINAM_ACCOUNT_ID` must be already set in your environment. If not configured by environment, follow these steps: 1. Register and obtain your API Key from [tokens page](https://tradeapi.finam.ru/docs/tokens) 2. Obtain your Account ID from your [Finam account dashboard](https://lk.finam.ru/) 3. Set environment variables: ```shell export FINAM_API_KEY="your_api_key_here" export FINAM_ACCOUNT_ID="your_account_id_here" ``` Obtain JWT token before using the API: ```shell export FINAM_JWT_TOKEN=$(curl -sL "https://api.finam.ru/v1/sessions" \ --header "Content-Type: application/json" \ --data '{"secret": "'"$FINAM_API_KEY"'"}' | jq -r '.token') ``` **Note:** Token expires after 15 minutes. Re-run this command if you receive authentication errors. ## Market assets ### List Available Exchanges and Equities **Symbol Format:** All symbols must be in `ticker@mic` format (e.g., `SBER@MISX`) **Base MIC Codes:** - `MISX` - Moscow Exchange - `RUSX` - RTS - `XNGS` - NASDAQ/NGS - `XNMS` - NASDAQ/NNS - `XNYS` - New York Stock Exchange View all supported exchanges with their MIC codes: ```shell jq -r '.exchanges[] | "\(.mic) - \(.name)"' assets/exchanges.json ``` List stocks available on a specific exchange: ```shell MIC="MISX" LIMIT=20 jq -r ".$MIC[:$LIMIT] | .[] | \"\(.symbol) - \(.name)\"" assets/equities.json ``` ### Get Asset Specification Fetch detailed specification for a specific instrument (lot size, price step, decimals, trading schedule, etc.): ```shell SYMBOL="SBER@MISX" curl -sL "https://api.finam.ru/v1/assets/$SYMBOL?account_id=$FINAM_ACCOUNT_ID" \ --header "Authorization: $FINAM_JWT_TOKEN" | jq ``` `account_id` is optional but recommended — returns account-specific fields (margin, available quantity, etc.). ### Search Assets Search instruments by ticker glob pattern and/or name substring: ```shell # By ticker glob python3 scripts/asset_search.py 'SBER*' # By name (case-insensitive substring) python3 scripts/asset_search.py --name 'apple' # By ticker glob + type filter python3 scripts/asset_search.py 'NG*' --type FUTURES # Search across all instruments (including archived) via /assets/all python3 scripts/asset_search.py 'NG*' --type FUTURES --active false ``` Available types: `EQUITIES`, `FUTURES`, `BONDS`, `FUNDS`, `SPREADS`, `OTHER`, `CURRENCIES`, `OPTIONS`, `SWAPS`, `INDICES` `--max=N` switches to `GET /v1/assets/all` with pagination (rate limit: 200 req/min). `--active false` includes archived instruments. ### Get Top N Stocks by Volume Pre-ranked lists of the 100 most liquid equities for each market, ordered by trading volume descending: ```shell N=10 jq -r ".[:$N] | .[] | \"\(.ticker) - \(.name)\"" assets/top_ru_equities.json ``` ```shell N=10 jq -r ".[:$N] | .[] | \"\(.ticker) - \(.name)\"" assets/top_us_equities.json ``` ## Account Management ### Get Account Portfolio Retrieve portfolio information including positions, balances, and P&L: ```shell curl -sL "https://api.finam.ru/v1/accounts/$FINAM_ACCOUNT_ID" \ --header "Authorization: $FINAM_JWT_TOKEN" | jq ``` ## Market Data ### Get Latest Quote Retrieve current bid/ask prices and last trade: ```shell SYMBOL="SBER@MISX" curl -sL "https://api.finam.ru/v1/instruments/$SYMBOL/quotes/latest" \ --header "Authorization: $FINAM_JWT_TOKEN" | jq ``` ### Get Order Book (Depth) View current market depth with bid/ask levels: ```shell SYMBOL="SBER@MISX" curl -sL "https://api.finam.ru/v1/instruments/$SYMBOL/orderbook" \ --header "Authorization: $FINAM_JWT_TOKEN" | jq ``` ### Get Recent Trades List the most recent executed trades: ```shell SYMBOL="SBER@MISX" curl -sL "https://api.finam.ru/v1/instruments/$SYMBOL/trades/latest" \ --header "Authorization: $FINAM_JWT_TOKEN" | jq ``` ### Get Historical Candles (OHLCV) Retrieve historical price data with specified timeframe: ```shell SYMBOL="SBER@MISX" TIMEFRAME="TIME_FRAME_D" START_TIME="2024-01-01T00:00:00Z" END_TIME="2024-04-01T00:00:00Z" curl -sL "https://api.finam.ru/v1/instruments/$SYMBOL/bars?timeframe=$TIMEFRAME&interval.start_time=$START_TIME&interval.end_time=$END_TIME" \ --header "Authorization: $FINAM_JWT_TOKEN" | jq ``` **Available Timeframes:** | Timeframe | Description | Max data depth (end_time - start_time) | |---|---|---| | `TIME_FRAME_UNSPECIFIED` | Not specified | — | | `TIME_FRAME_M1` | 1 minute | 7 days | | `TIME_FRAME_M5` | 5 minutes | 30 days | | `TIME_FRAME_M15` | 15 minutes | 30 days | | `TIME_FRAME_M30` | 30 minutes | 30 days | | `TIME_FRAME_H1` | 1 hour | 30 days | | `TIME_FRAME_H2` | 2 hours | 30 days | | `TIME_FRAME_H4` | 4 hours | 30 days | | `TIME_FRAME_H8` | 8 hours | 30 days | | `TIME_FRAME_D` | Day | 365 days | | `TIME_FRAME_W` | Week | ~5 years | | `TIME_FRAME_MN` | Month | ~5 years | | `TIME_FRAME_QR` | Quarter | ~5 years | > **Note:** The max data depth is the maximum allowed range for `end_time - start_time`. If the range exceeds the limit, the API returns empty data. **Date Format (RFC 3339):** - Format: `YYYY-MM-DDTHH:MM:SSZ` or `YYYY-MM-DDTHH:MM:SS+HH:MM` - `start_time` - Inclusive (interval start, included in results) - `end_time` - Exclusive (interval end, NOT included in results) - Examples: - `2024-01-15T10:30:00Z` (UTC) - `2024-01-15T10:30:00+03:00` (Moscow time, UTC+3) ## News ### Get Latest Market News Fetch and display the latest news headlines. No JWT token required. Russian market news ```shell curl -sL "https://www.finam.ru/analysis/conews/rsspoint/" | python3 -c " import sys, xml.etree.ElementTree as ET root = ET.parse(sys.stdin).getroot() for item in reversed(root.findall('.//item')): print(f'* {item.findtext('title','')}. {item.findtext('description','').split('...')[0]}') " ``` US market news ```shell curl -sL "https://www.finam.ru/international/advanced/rsspoint/" | python3 -c " import sys, xml.etree.ElementTree as ET root = ET.parse(sys.stdin).getroot() for item in reversed(root.findall('.//item')): print(f'* {item.findtext('title','')}. {item.findtext('description','').split('...')[0]}') " ``` **Parameters:** - Change `[:10]` to any number to control how many headlines to display ## Order Management > **IMPORTANT:** Before placing or cancelling any order, you MUST explicitly confirm the details with the user and > receive their approval. State the full order parameters (symbol, side, quantity, type, price) and wait for confirmation > before executing. ### Place Order **Order Types:** - `ORDER_TYPE_MARKET` - Market order (executes immediately, no `limit_price` required) - `ORDER_TYPE_LIMIT` - Limit order (requires `limit_price`) ```shell curl -sL "https://api.finam.ru/v1/accounts/$FINAM_ACCOUNT_ID/orders" \ --header "Authorization: $FINAM_JWT_TOKEN" \ --header "Content-Type: application/json" \ --data "$(jq -n \ --arg symbol "SBER@MISX" \ --arg quantity "10" \ --arg side "SIDE_BUY" \ --arg type "ORDER_TYPE_LIMIT" \ --arg price "310.50" \ '{symbol: $symbol, quantity: {value: $quantity}, side: $side, type: $type, limit_price: {value: $price}}')" \ | jq ``` **Parameters:** - `symbol` - Instrument (e.g., `SBER@MISX`) - `quantity.value` - Number of shares/contracts - `side` - `SIDE_BUY` or `SIDE_SELL` - `type` - `ORDER_TYPE_MARKET` or `ORDER_TYPE_LIMIT` - `limit_price` - Only for `ORDER_TYPE_LIMIT` (omit for market orders) ### Get Order Status Check the status of a specific order: ```shell ORDER_ID="12345678" curl -sL "https://api.finam.ru/v1/accounts/$FINAM_ACCOUNT_ID/orders/$ORDER_ID" \ --header "Authorization: $FINAM_JWT_TOKEN" | jq ``` ### Cancel Order Cancel a pending order: ```shell ORDER_ID="12345678" curl -sL --request DELETE "https://api.finam.ru/v1/accounts/$FINAM_ACCOUNT_ID/orders/$ORDER_ID" \ --header "Authorization: $FINAM_JWT_TOKEN" | jq ``` ## Volatility Scanner Scans the top-100 stocks for a given market and prints the most volatile ones based on annualized historical volatility (close-to-close, last 60 days). **Usage:** ```shell python3 scripts/volatility.py [ru|us] [N] ``` **Arguments:** - `ru` / `us` — market to scan (default: `ru`) - `N` — number of top results to display (default: `10`) **Examples:** ```shell # Top 10 most volatile Russian stocks python3 scripts/volatility.py ru 10 # Top 5 most volatile US stocks python3 scripts/volatility.py us 5 ``` All scripts support `--help` for usage details (e.g. `python3 scripts/volatility.py --help`). ## REST vs gRPC — When to Use Which **Use REST when:** - Fetching historical OHLCV data for backtesting or analysis - Retrieving account info, positions, balances, and trade history - Searching instruments or fetching asset specifications - Placing or cancelling a one-off order where 100–200 ms latency is acceptable - Checking order status after placement **Use gRPC when:** - Subscribing to real-time market data: quotes, order book, trade feed - Processing live bar data (M1, M5) for signal generation in event-driven strategies - Monitoring own orders and trades in real time via subscription - Your strategy requires minimal latency for rapid order placement/cancellation - Building long-running trading bots that need persistent, auto-reconnecting connections For gRPC, use the [FinamPy](https://github.com/cia76/FinamPy) Python library ( `pip install git+https://github.com/cia76/FinamPy.git`). Full usage reference: [gRPC Python Reference](references/GRPC.md) --- For full REST API details, use the local file: [REST Reference](references/REST.md) For extra gRPC API details, fetch from: [gRPC Reference](https://tradeapi.finam.ru/docs/grpc/)

path: assets/top_us_equities.json
content: ticker: NVDA@XNGS
name: NVIDIA Corporation

ticker: PLUG@RUSX
name: Plug Power Inc.

ticker: RIG@XNYS
name: Transocean Ltd.

ticker: AMZN@XNGS
name: Amazon.com, Inc.

ticker: INTC@XNGS
name: Intel Corporation

ticker: TSLA@XNGS
name: Tesla, Inc.

ticker: RIVN@XNGS
name: Rivian Automotive, Inc.

ticker: VALE@XNYS
name: Vale S.A.

ticker: NCLH@XNYS
name: Norwegian Cruise Line Holdings Ltd.

ticker: ZIM@XNYS
name: ZIM Integrated Shipping Services Ltd.

ticker: F@XNYS
name: Ford Motor Company

ticker: SNAP@XNYS
name: Snap Inc.

ticker: SOFI@XNGS
name: SoFi Technologies, Inc.

ticker: PLTR@XNGS
name: Palantir Technologies Inc.

ticker: GOOGL@XNGS
name: Alphabet Inc.

ticker: MARA@RUSX
name: MARA Holdings, Inc.

ticker: AAPL@XNGS
name: Apple Inc.

ticker: NFLX@XNGS
name: Netflix, Inc.

ticker: BBD@XNYS
name: Banco Bradesco S.A.

ticker: AMD@XNGS
name: Advanced Micro Devices, Inc.

ticker: KVUE@XNYS
name: Kenvue Inc.

ticker: HOOD@XNGS
name: Robinhood Markets, Inc.

ticker: AAL@XNGS
name: American Airlines Group Inc.

ticker: PATH@XNYS
name: UiPath, Inc.

ticker: NOK@XNYS
name: Nokia Oyj

ticker: CIFR@XNGS
name: Cipher Mining Inc.

ticker: LYG@XNYS
name: Lloyds Banking Group plc

ticker: NIO@XNYS
name: NIO Inc.

ticker: NOW@XNYS
name: ServiceNow, Inc.

ticker: MU@XNGS
name: Micron Technology, Inc.

ticker: OPEN@XNGS
name: Opendoor Technologies Inc.

ticker: GRAB@XNGS
name: Grab Holdings Limited

ticker: SMR@XNYS
name: NuScale Power Corporation

ticker: ACHR@XNYS
name: Archer Aviation Inc.

ticker: PFE@XNYS
name: Pfizer Inc.

ticker: WMT@XNGS
name: Walmart Inc.

ticker: BAC@XNYS
name: Bank of America Corporation

ticker: CDE@XNYS
name: Coeur Mining, Inc.

ticker: WBD@XNGS
name: Warner Bros. Discovery, Inc.

ticker: MSFT@XNGS
name: Microsoft Corporation

ticker: QBTS@XNYS
name: D-Wave Quantum Inc.

ticker: CNH@XNYS
name: CNH Industrial N.V.

ticker: IREN@XNGS
name: IREN Limited

ticker: SLB@XNYS
name: SLB N.V.

ticker: GOOG@XNGS
name: Alphabet Inc.

ticker: PINS@XNYS
name: Pinterest, Inc.

ticker: HL@XNYS
name: Hecla Mining Company

ticker: GIS@XNYS
name: General Mills, Inc.

ticker: FSLY@XNGS
name: Fastly, Inc.

ticker: NU@XNYS
name: Nu Holdings Ltd.

ticker: INFY@XNYS
name: Infosys Limited

ticker: DKNG@XNGS
name: DraftKings Inc.

ticker: CLSK@RUSX
name: CleanSpark, Inc.

ticker: HIMS@XNYS
name: Hims &amp; Hers Health, Inc.

ticker: BTE@XNYS
name: Baytex Energy Corp.

ticker: ITUB@XNYS
name: Itaú Unibanco Holding S.A.

ticker: AG@XNYS
name: First Majestic Silver Corp.

ticker: SNDK@XNGS
name: Sandisk Corporation

ticker: T@MISX
name: AT&amp;T Inc.

ticker: CPNG@XNYS
name: Coupang, Inc.

ticker: ET@XNYS
name: Energy Transfer LP

ticker: APLD@XNGS
name: Applied Digital Corporation

ticker: SMCI@XNGS
name: Super Micro Computer, Inc.

ticker: CAG@XNYS
name: Conagra Brands, Inc.

ticker: VZ@XNYS
name: Verizon Communications Inc.

ticker: PYPL@XNGS
name: PayPal Holdings, Inc.

ticker: HBAN@XNGS
name: Huntington Bancshares Incorporated

ticker: MSTR@XNGS
name: Strategy Inc

ticker: CRWV@XNGS
name: CoreWeave, Inc.

ticker: U@XNYS
name: Unity Software Inc.

ticker: MASI@XNGS
name: Masimo Corporation

ticker: SOUN@XNMS
name: SoundHound AI, Inc.

ticker: COIN@XNGS
name: Coinbase Global, Inc.

ticker: KHC@XNGS
name: The Kraft Heinz Company

ticker: CWAN@XNYS
name: Clearwater Analytics Holdings, Inc.

ticker: CSCO@XNGS
name: Cisco Systems, Inc.

ticker: OWL@XNYS
name: Blue Owl Capital Inc.

ticker: HPQ@XNYS
name: HP Inc.

ticker: IBRX@XNGS
name: ImmunityBio, Inc.

ticker: ORCL@XNYS
name: Oracle Corporation

ticker: RIOT@RUSX
name: Riot Platforms, Inc.

ticker: CMCSA@XNGS
name: Comcast Corporation

ticker: RKT@XNYS
name: Rocket Companies, Inc.

ticker: B@XNYS
name: Barrick Mining Corporation

ticker: UBER@XNYS
name: Uber Technologies, Inc.

ticker: CCL@XNYS
name: Carnival Corporation &amp; plc

ticker: AGNC@XNGS
name: AGNC Investment Corp.

ticker: SHOP@XNGS
name: Shopify Inc.

ticker: TOST@XNYS
name: Toast, Inc.

ticker: AVGO@XNGS
name: Broadcom Inc.

ticker: CLF@XNYS
name: Cleveland-Cliffs Inc.

ticker: CMG@XNYS
name: Chipotle Mexican Grill, Inc.

ticker: STLA@XNYS
name: Stellantis N.V.

ticker: ABEV@XNYS
name: Ambev S.A.

ticker: USAR@XNMS
name: USA Rare Earth, Inc.

ticker: XOM@XNYS
name: Exxon Mobil Corporation

ticker: MDT@XNYS
name: Medtronic plc

ticker: PSKY@XNGS
name: Paramount Skydance Corporation

ticker: PCG@XNYS
name: PG&amp;E Corporation

ticker: QS@XNGS
name: QuantumScape Corporation

ticker: IONQ@XNYS
name: IonQ, Inc.

ticker: EXK@XNYS
name: Endeavour Silver Corp.

ticker: IOT@XNYS
name: Samsara Inc.

ticker: NBIS@XNGS
name: Nebius Group N.V.

ticker: PBR@XNYS
name: Petróleo Brasileiro S.A. - Petrobras

ticker: KGC@XNYS
name: Kinross Gold Corporation

ticker: TTD@XNMS
name: The Trade Desk, Inc.

ticker: SAN@XNYS
name: Banco Santander, S.A.

ticker: BCS@XNYS
name: Barclays PLC

ticker: CVE@XNYS
name: Cenovus Energy Inc.

ticker: FCX@XNYS
name: Freeport-McMoRan Inc.

ticker: CRM@XNYS
name: Salesforce, Inc.

ticker: KO@XNYS
name: The Coca-Cola Company

ticker: LYFT@XNGS
name: Lyft, Inc.

ticker: KD@XNYS
name: Kyndryl Holdings, Inc.

ticker: UAA@XNYS
name: Under Armour, Inc.

ticker: TSM@XNYS
name: Taiwan Semiconductor Manufacturing Company Limited

ticker: AUR@XNGS
name: Aurora Innovation, Inc.

ticker: VG@XNYS
name: Venture Global, Inc.

ticker: COMP@XNYS
name: Compass, Inc.

ticker: JOBY@XNYS
name: Joby Aviation, Inc.

ticker: JBLU@XNGS
name: JetBlue Airways Corporation

ticker: FIG@XNYS
name: Figma, Inc.

ticker: FISV@XNGS
name: Fiserv, Inc.

ticker: LUV@XNYS
name: Southwest Airlines Co.

ticker: BSX@XNYS
name: Boston Scientific Corporation

ticker: PG@XNYS
name: The Procter &amp; Gamble Company

ticker: KEY@XNYS
name: KeyCorp

ticker: META@XNGS
name: Meta Platforms, Inc.

ticker: GGB@XNYS
name: Gerdau S.A.

ticker: CART@XNGS
name: Maplebear Inc.

ticker: BMY@XNYS
name: Bristol-Myers Squibb Company

ticker: HLN@XNYS
name: Haleon plc

ticker: PANW@XNGS
name: Palo Alto Networks, Inc.

ticker: UUUU@RUSX
name: Energy Fuels Inc.

ticker: NVO@XNYS
name: Novo Nordisk A/S

ticker: CNQ@XNYS
name: Canadian Natural Resources Limited

ticker: TEVA@XNYS
name: Teva Pharmaceutical Industries Limited

ticker: CSX@XNGS
name: CSX Corporation

ticker: ASTS@XNGS
name: AST SpaceMobile, Inc.

ticker: ENB@XNYS
name: Enbridge Inc.

ticker: LUMN@XNYS
name: Lumen Technologies, Inc.

ticker: CORZ@XNGS
name: Core Scientific, Inc.

ticker: UWMC@XNYS
name: UWM Holdings Corporation

ticker: DOW@XNYS
name: Dow Inc.

ticker: MRNA@XNGS
name: Moderna, Inc.

ticker: NKE@XNYS
name: NIKE, Inc.

ticker: KMI@XNYS
name: Kinder Morgan, Inc.

ticker: HDB@XNYS
name: HDFC Bank Limited

path: assets/top_ru_equities.json
content: ticker: VTBR@MISX
name: ВТБ ао

ticker: SBER@MISX
name: Сбербанк

ticker: GAZP@MISX
name: ГАЗПРОМ ао

ticker: GMKN@MISX
name: ГМКНорНик

ticker: T@MISX
name: Т-Техно ао

ticker: PLZL@MISX
name: Полюс

ticker: YDEX@MISX
name: ЯНДЕКС

ticker: LKOH@MISX
name: ЛУКОЙЛ

ticker: SVCB@MISX
name: Совкомбанк

ticker: AFKS@MISX
name: Система ао

ticker: SMLT@MISX
name: Самолет ао

ticker: MAGN@MISX
name: ММК

ticker: NVTK@MISX
name: Новатэк ао

ticker: OZON@MISX
name: Озон

ticker: PIKK@MISX
name: ПИК ао

ticker: ROSN@MISX
name: Роснефть

ticker: UGLD@MISX
name: ЮГК

ticker: X5@MISX
name: КЦ ИКС 5

ticker: RUAL@MISX
name: РУСАЛ ао

ticker: AFLT@MISX
name: Аэрофлот

ticker: SPBE@MISX
name: СПБ Биржа

ticker: MOEX@MISX
name: МосБиржа

ticker: NLMK@MISX
name: НЛМК ао

ticker: MTSS@MISX
name: МТС-ао

ticker: MGNT@MISX
name: Магнит ао

ticker: MTLR@MISX
name: Мечел ао

ticker: VKCO@MISX
name: МКПАО ВК

ticker: SNGSP@MISX
name: Сургнфгз-п

ticker: DATA@MISX
name: iАренадата

ticker: TATN@MISX
name: Татнфт 3ао

ticker: IRAO@MISX
name: ИнтерРАОао

ticker: HEAD@MISX
name: Хэдхантер

ticker: SBERP@MISX
name: Сбербанк-п

ticker: TRNFP@MISX
name: Транснф ап

ticker: CHMF@MISX
name: СевСт-ао

ticker: UPRO@MISX
name: Юнипро ао

ticker: RTKM@MISX
name: Ростел -ао

ticker: SNGS@MISX
name: Сургнфгз

ticker: RNFT@MISX
name: РуссНфт ао

ticker: WUSH@MISX
name: iВУШХолднг

ticker: SGZH@MISX
name: Сегежа

ticker: ALRS@MISX
name: АЛРОСА ао

ticker: BSPB@MISX
name: БСП ао

ticker: PHOR@MISX
name: ФосАгро ао

ticker: SELG@MISX
name: Селигдар

ticker: RENI@MISX
name: Ренессанс

ticker: MBNK@MISX
name: МТС Банк

ticker: SPBE@MISX
name: ПАО "СПБ Биржа"

ticker: POSI@MISX
name: iПозитив

ticker: TATNP@MISX
name: Татнфт 3ап

ticker: SFIN@MISX
name: ЭсЭфАй ао

ticker: TGKN@MISX
name: ТГК-14

ticker: SOFL@MISX
name: iСофтлайн

ticker: FEES@MISX
name: Россети

ticker: BELU@MISX
name: НоваБев ао

ticker: EUTR@MISX
name: ЕвроТранс

ticker: MTLRP@MISX
name: Мечел ап

ticker: HYDR@MISX
name: РусГидро

ticker: CNRU@MISX
name: Циан

ticker: NMTP@MISX
name: НМТП ао

ticker: FLOT@MISX
name: Совкомфлот

ticker: DIAS@MISX
name: iДиасофт

ticker: ENPG@MISX
name: ЭН+ГРУП ао

ticker: TGKA@MISX
name: ТГК-1

ticker: LENT@MISX
name: Лента ао

ticker: RAGR@MISX
name: Русагро

ticker: MSNG@MISX
name: +МосЭнерго

ticker: ASTR@MISX
name: Астра ао

ticker: ETLN@MISX
name: ЭталонГруп

ticker: VSEH@MISX
name: ВИ.ру

ticker: IRKT@MISX
name: Яковлев-3

ticker: TRMK@MISX
name: ТМК ао

ticker: PRMD@MISX
name: iПРОМОМЕД

ticker: LSRG@MISX
name: ЛСР ао

ticker: CBOM@MISX
name: МКБ ао

ticker: MDMG@MISX
name: MDMG-ао

ticker: AQUA@MISX
name: ИНАРКТИКА

ticker: OZPH@MISX
name: iОзонФарм

ticker: IVAT@MISX
name: iИВА

ticker: RTKMP@MISX
name: Ростел -ап

ticker: MRKC@MISX
name: РоссЦентр

ticker: LSNGP@MISX
name: РСетиЛЭ-п

ticker: MRKU@MISX
name: Россети Ур

ticker: LEAS@MISX
name: Европлан

ticker: PMSBP@MISX
name: ПермьЭнС-п

ticker: UWGN@MISX
name: ОВК ао

ticker: FESH@MISX
name: ДВМП ао

ticker: BANEP@MISX
name: Башнефт ап

ticker: UNAC@MISX
name: iАвиастКао

ticker: LPSB@MISX
name: ЛЭСК ао

ticker: MVID@MISX
name: М.видео

ticker: FIXR@MISX
name: Фикс Прайс

ticker: RASP@MISX
name: Распадская

ticker: NKHP@MISX
name: НКХП ао

ticker: OGKB@MISX
name: ОГК-2 ао

ticker: ROLO@MISX
name: Русолово

ticker: CARM@MISX
name: СТГ

ticker: SVAV@MISX
name: СОЛЛЕРС

ticker: LNZLP@MISX
name: Лензол. ап

ticker: BANE@MISX
name: Башнефт ао

ticker: MGKL@MISX
name: МГКЛ

ticker: ZAYM@MISX
name: Займер ао

ticker: MRKP@MISX
name: РСетиЦП ао

ticker: PMSB@MISX
name: ПермьЭнСб

ticker: GAZA@MISX
name: ГАЗ ао

ticker: VSMO@MISX
name: ВСМПО-АВСМ

ticker: NSVZ@MISX
name: iНаукаСвяз

ticker: GTRK@MISX
name: ГТМ ао

ticker: AKRN@MISX
name: Акрон

ticker: MRKV@MISX
name: РсетВол ао

ticker: KMAZ@MISX
name: КАМАЗ

ticker: HNFG@MISX
name: ХЭНДЕРСОН

ticker: LNZL@MISX
name: Лензолото

ticker: MSRS@MISX
name: РСетиМР ао

ticker: STSBP@MISX
name: СтаврЭнСбп

ticker: LSNG@MISX
name: РСетиЛЭ

ticker: ELFV@MISX
name: ЭЛ5Энер ао

ticker: GAZAP@MISX
name: ГАЗ ап

ticker: GEMC@MISX
name: МКПАО ЮМГ

ticker: DELI@MISX
name: iКаршеринг

ticker: ABIO@MISX
name: iАРТГЕН ао

ticker: MRKZ@MISX
name: РСетиСЗ ао

ticker: GECO@MISX
name: iГЕНЕТИКО

ticker: MSTT@MISX
name: Мостотрест

ticker: TTLK@MISX
name: Таттел. ао

ticker: NKNCP@MISX
name: НКНХ ап

ticker: RBCM@MISX
name: ГК РБК ао

ticker: MRKS@MISX
name: РсетСиб ао

ticker: JNOS@MISX
name: Славн-ЯНОС

ticker: LIFE@MISX
name: Фармсинтез

ticker: RKKE@MISX
name: ЭнергияРКК

ticker: MRKY@MISX
name: РоссЮг ао

ticker: GCHE@MISX
name: ЧеркизГ-ао

ticker: APTK@MISX
name: Аптеки36и6

ticker: JNOSP@MISX
name: Слав-ЯНОСп

ticker: NAUK@MISX
name: iНПОНаука

ticker: ELMT@MISX
name: ПАО "Элемент"

ticker: BLNG@MISX
name: Белон ао

ticker: YRSB@MISX
name: ТНСэнЯр

ticker: YAKG@MISX
name: ЯТЭК ао

ticker: VLHZ@MISX
name: ВХЗ-ао

ticker: KROT@MISX
name: КрасОкт-ао

ticker: ASSB@MISX
name: АстрЭнСб

ticker: NFAZ@MISX
name: НЕФАЗ

ticker: ELMT@MISX
name: Элемент

ticker: TGKB@MISX
name: ТГК-2

ticker: SVETP@MISX
name: Светофор п

ticker: ABRD@MISX
name: АбрауДюрсо

ticker: STSB@MISX
name: СтаврЭнСб

ticker: DIOD@MISX
name: ЗаводДИОД

ticker: BSPBP@MISX
name: БСП ап

ticker: VEON-RX@MISX
name: VEON

ticker: KROTP@MISX
name: КрасОкт-1п

ticker: SARE@MISX
name: СаратЭн-ао

ticker: YRSBP@MISX
name: ТНСэнЯр-п

ticker: IGSTP@MISX
name: Ижсталь ап

ticker: KZOSP@MISX
name: ОргСинт ап

ticker: RGSS@MISX
name: РГС СК ао

ticker: SAGO@MISX
name: СамарЭн-ао

ticker: VGSBP@MISX
name: ВолгЭнСб-п

ticker: SVET@MISX
name: Светофор

ticker: MRSB@MISX
name: МордЭнСб

ticker: UNKL@MISX
name: ЮУНК ао

ticker: TGKBP@MISX
name: ТГК-2 ап

ticker: VTGK@MISX
name: ТПлюс ао

ticker: PRFN@MISX
name: ТЕПЛАНТ ао

ticker: USBN@MISX
name: УралСиб ао

ticker: APRI@MISX
name: АПРИ

ticker: NKNC@MISX
name: НКНХ ао

ticker: KAZT@MISX
name: Куйбазот

ticker: KRSB@MISX
name: Красэсб ао

ticker: PRMB@MISX
name: Приморье

ticker: KZOS@MISX
name: ОргСинт ао

ticker: VSYDP@MISX
name: ВыбСудЗ ап

ticker: DVEC@MISX
name: ДЭК ао

ticker: SAREP@MISX
name: СаратЭн-ап

ticker: TATN@MISX
name: ПАО 'Татнефть' им. В.Д. Шашина, ао

ticker: KRKN@MISX
name: СаратНПЗ

ticker: EELT@MISX
name: ЕвроЭлтех

ticker: BRZL@MISX
name: БурЗолото

ticker: TASB@MISX
name: ТамбЭнСб

ticker: TUZA@MISX
name: ТЗА ао

ticker: KLVZ@MISX
name: Кристалл

ticker: GEMA@MISX
name: iММЦБ ао

ticker: WTCM@MISX
name: ЦМТ ао

ticker: MTPVP@MISX
name: Ванино-ап

ticker: MGTSP@MISX
name: МГТС-4ап

ticker: CHMK@MISX
name: ЧМК ао

ticker: AVAN@MISX
name: Авангрд-ао

ticker: KLSB@MISX
name: КалужскСК

ticker: RZSB@MISX
name: РязЭнСб

ticker: KCHE@MISX
name: КамчатЭ ао

ticker: YKEN@MISX
name: Якутскэнрг

ticker: ZILL@MISX
name: ЗИЛ ао

ticker: VRSB@MISX
name: ТНСэнВорон

ticker: KRSBP@MISX
name: Красэсб ап

ticker: RTSB@MISX
name: ТНСэнРст

ticker: TORSP@MISX
name: РСТомск ап

ticker: CNTLP@MISX
name: Телеграф-п

ticker: CHKZ@MISX
name: ЧКПЗ ао

path: assets/exchanges.json
content: {
    "exchanges": [
        {
            "mic": "MISX",
            "name": "MOSCOW EXCHANGE - ALL MARKETS"
        },
        {
            "mic": "XNGS",
            "name": "NASDAQ/NGS (GLOBAL SELECT MARKET)"
        },
        {
            "mic": "XNYS",
            "name": "NEW YORK STOCK EXCHANGE, INC."
        },
        {
            "mic": "XNMS",
            "name": "NASDAQ/NMS (GLOBAL MARKET)"
        },
        {
            "mic": "XNCM",
            "name": "NASDAQ CAPITAL MARKET"
        },
        {
            "mic": "BATS",
            "name": "CBOE BZX U.S. EQUITIES EXCHANGE"
        },
        {
            "mic": "ARCX",
            "name": "NYSE ARCA"
        },
        {
            "mic": "XASE",
            "name": "NYSE MKT LLC"
        },
        {
            "mic": "XHKG",
            "name": "HONG KONG EXCHANGES AND CLEARING LTD"
        },
        {
            "mic": "XTKS",
            "name": "TOKYO STOCK EXCHANGE"
        },
        {
            "mic": "XPAR",
            "name": "EURONEXT - EURONEXT PARIS"
        },
        {
            "mic": "XLON",
            "name": "LONDON STOCK EXCHANGE"
        },
        {
            "mic": "XBRU",
            "name": "EURONEXT - EURONEXT BRUSSELS"
        },
        {
            "mic": "_SCI",
            "name": "Synthetic Composite Index"
        },
        {
            "mic": "XLOM",
            "name": "LONDON STOCK EXCHANGE - MTF"
        },
        {
            "mic": "RUSX",
            "name": "NON-PROFIT PARTNERSHIP FOR THE DEVELOPMENT OF FINANCIAL MARKET RTS"
        },
        {
            "mic": "PINX",
            "name": "OTC PINK MARKETPLACE"
        },
        {
            "mic": "XNYM",
            "name": "NEW YORK MERCANTILE EXCHANGE"
        },
        {
            "mic": "XAMS",
            "name": "EURONEXT - EURONEXT AMSTERDAM"
        },
        {
            "mic": "XETR",
            "name": "XETRA"
        },
        {
            "mic": "XMAD",
            "name": "BOLSA DE MADRID"
        },
        {
            "mic": "XGAT",
            "name": "TRADEGATE EXCHANGE - FREIVERKEHR"
        },
        {
            "mic": "XCEC",
            "name": "COMMODITIES EXCHANGE CENTER"
        },
        {
            "mic": "XCME",
            "name": "CHICAGO MERCANTILE EXCHANGE"
        },
        {
            "mic": "RTSX",
            "name": "MOSCOW EXCHANGE - DERIVATIVES MARKET"
        },
        {
            "mic": "_SPBZ",
            "name": "SPB OTC issues"
        },
        {
            "mic": "_TRES",
            "name": "Treasuries OTC issues"
        },
        {
            "mic": "_EURB",
            "name": "Eurobonds OTC issues"
        },
        {
            "mic": "_CMF",
            "name": "Сredit Mutual Funds"
        },
        {
            "mic": "XSHG",
            "name": "SHANGHAI STOCK EXCHANGE"
        },
        {
            "mic": "XSHE",
            "name": "SHENZHEN STOCK EXCHANGE"
        },
        {
            "mic": "_MMBZ",
            "name": "MOEX OTC issues"
        },
        {
            "mic": "_NPRO",
            "name": "NTPRO"
        },
        {
            "mic": "_SMMA",
            "name": "Currency SMMA"
        },
        {
            "mic": "_CRYP",
            "name": "OTC Options Crypto"
        },
        {
            "mic": "XNAS",
            "name": "NASDAQ - ALL MARKETS"
        }
    ]
}

path: scripts/asset_search.py
content: #!/usr/bin/env python3
"""Search Finam instruments by ticker glob pattern or name substring."""

import argparse
import fnmatch
import sys
import urllib.parse

import httpx

import utils
from utils import BASE_URL, dprint, get_token


VALID_TYPES = {"EQUITIES", "FUTURES", "BONDS", "FUNDS", "SPREADS", "OTHER", "CURRENCIES", "OPTIONS", "SWAPS", "INDICES"}


def parse_args():
    parser = argparse.ArgumentParser(
        description="Search Finam instruments by ticker glob pattern and/or name substring.",
        epilog=(
            "examples:\n"
            "  asset_search.py 'SBER*'\n"
            "  asset_search.py 'NG*' --type FUTURES --active false\n"
            "  asset_search.py --name 'natural gas' --type FUTURES\n"
        ),
        formatter_class=argparse.RawDescriptionHelpFormatter,
    )
    parser.add_argument("pattern", nargs="?", help="glob pattern matched against ticker and symbol (e.g. 'SBER*', 'NG*')")
    parser.add_argument("--name", metavar="QUERY", help="case-insensitive substring search on instrument name")
    parser.add_argument("--type", metavar="TYPE", choices=VALID_TYPES, type=str.upper, help=f"filter by type: {', '.join(sorted(VALID_TYPES))}")
    parser.add_argument("--active", metavar="true|false", default="true", choices=("true", "false"), help="include archived instruments (default: true)")
    parser.add_argument("--max", metavar="N", type=int, default=30_000, help="max assets to fetch via /assets/all when --active false (default: 30000)")
    parser.add_argument("--debug", action="store_true", help="verbose output")

    args = parser.parse_args()

    if args.pattern is None and args.name is None:
        parser.error("at least one of pattern or --name is required")

    if args.debug:
        utils.DEBUG = True

    return args.pattern, args.name.lower() if args.name else None, args.type, args.active == "true", args.max


def fetch_assets(token):
    url = f"{BASE_URL}/assets"
    try:
        with httpx.Client(http2=True) as client:
            resp = client.get(url, headers={"Authorization": token})
            resp.raise_for_status()
            data = resp.json()
    except httpx.HTTPStatusError as e:
        print(f"Error: HTTP {e.response.status_code} fetching assets", file=sys.stderr)
        sys.exit(1)
    assets = data.get("assets") or []
    dprint(f"  Fetched {len(assets)} assets")
    return assets


def fetch_assets_all(token, only_active, max_assets):
    assets = []
    cursor = ""
    with httpx.Client(http2=True) as client:
        while True:
            params = {"only_active": "true" if only_active else "false"}
            if cursor:
                params["cursor"] = cursor
            url = f"{BASE_URL}/assets/all?" + urllib.parse.urlencode(params)
            try:
                resp = client.get(url, headers={"Authorization": token})
                resp.raise_for_status()
                data = resp.json()
            except httpx.HTTPStatusError as e:
                print(f"Error: HTTP {e.response.status_code} fetching assets", file=sys.stderr)
                sys.exit(1)

            batch = data.get("assets") or []
            assets.extend(batch)
            dprint(f"  Fetched {len(batch)} assets (total: {len(assets)})")

            if max_assets and len(assets) >= max_assets:
                assets = assets[:max_assets]
                break

            next_cursor = data.get("next_cursor", "")
            if not next_cursor or next_cursor == cursor:
                break
            cursor = next_cursor

    return assets


def main():
    pattern, name_query, type_filter, only_active, max_assets = parse_args()

    [REDACTED_SECRET])

    if not only_active:
        dprint(f"Fetching all assets (only_active=false, max={max_assets})...")
        assets = fetch_assets_all(token, only_active=False, max_assets=max_assets)
    else:
        dprint("Fetching active assets...")
        assets = fetch_assets(token)
    dprint(f"Total assets fetched: {len(assets)}")

    matches = []
    for a in assets:
        if type_filter and a.get("type", "").upper() != type_filter:
            continue
        ticker_match = pattern and (
            fnmatch.fnmatch(a.get("ticker", ""), pattern)
            or fnmatch.fnmatch(a.get("symbol", ""), pattern)
        )
        name_match = name_query and name_query in a.get("name", "").lower()
        if pattern and name_query and ticker_match and name_match:
            matches.append(a)
        elif pattern and not name_query and ticker_match:
            matches.append(a)
        elif name_query and not pattern and name_match:
            matches.append(a)

    parts = []
    if pattern:
        parts.append(f"'{pattern}'")
    if name_query:
        parts.append(f"name='{name_query}'")
    if type_filter:
        parts.append(f"type={type_filter}")
    filter_desc = " ".join(parts)

    if not matches:
        print(f"No instruments found matching {filter_desc}.")
        return

    print(f"\nFound {len(matches)} instrument(s) matching {filter_desc}:\n")
    header = f"{'Ticker':<16}{'Symbol':<20}{'MIC':<10}{'Type':<16}{'Name'}"
    print(header)
    print("\u2500" * (len(header) + 20))
    for a in matches:
        archived = " [archived]" if a.get("is_archived") else ""
        print(
            f"{a.get('ticker', ''):<16}"
            f"{a.get('symbol', ''):<20}"
            f"{a.get('mic', ''):<10}"
            f"{a.get('type', ''):<16}"
            f"{a.get('name', '')}{archived}"
        )


if __name__ == "__main__":
    main()

path: scripts/utils.py
content: import json
import os
import sys
import urllib.request
from pathlib import Path

BASE_URL = "https://api.finam.ru/v1"
ASSETS_DIR = Path(__file__).parent.parent / "assets"
DEBUG = False


def dprint(*args, **kwargs):
    if DEBUG:
        print(*args, **kwargs)


def get_token():
    cached = os.environ.get("FINAM_JWT_TOKEN")
    if cached:
        dprint("Using JWT token from FINAM_JWT_TOKEN.")
        return cached

    [REDACTED_SECRET]"FINAM_API_KEY")
    if not [REDACTED_SECRET]"Error: FINAM_API_KEY environment variable is not set.", file=sys.stderr)
        sys.exit(1)

    dprint("Obtaining JWT token...")
    url = f"{BASE_URL}/sessions"
    payload = json.dumps({"secret": api_key}).encode()
    req = urllib.request.Request(
        url,
        data=payload,
        headers={"Content-Type": "application/json"},
        method="POST",
    )
    try:
        with urllib.request.urlopen(req) as resp:
            data = json.loads(resp.read())
    except Exception as e:
        print(f"Error: failed to authenticate: {e}", file=sys.stderr)
        sys.exit(1)
    token = data["token"]
    os.environ["FINAM_JWT_TOKEN"] = token
    dprint("Token obtained and saved to FINAM_JWT_TOKEN.")
    return token


def load_equities(market):
    path = ASSETS_DIR / f"top_{market}_equities.json"
    with open(path) as f:
        return json.load(f)


path: scripts/volatility.py
content: #!/usr/bin/env python3
"""Volatility scanner for Finam top-100 stocks."""

import argparse
import json
import math
import sys
from concurrent.futures import ThreadPoolExecutor, as_completed
from datetime import datetime, timedelta, timezone

import urllib.request
import urllib.error

import utils
from utils import BASE_URL, dprint, get_token, load_equities


def parse_args():
    parser = argparse.ArgumentParser(
        description="Scan top-100 stocks for annualized historical volatility (last 60 days).",
        epilog=(
            "examples:\n"
            "  volatility.py ru 10\n"
            "  volatility.py us 5\n"
        ),
        formatter_class=argparse.RawDescriptionHelpFormatter,
    )
    parser.add_argument("market", nargs="?", default="ru", choices=("ru", "us"), help="market to scan (default: ru)")
    parser.add_argument("n", nargs="?", default=10, type=int, metavar="N", help="number of top results to display (default: 10)")
    parser.add_argument("--debug", action="store_true", help="verbose output")

    args = parser.parse_args()

    if args.debug:
        utils.DEBUG = True

    return args.market, args.n


def fetch_bars(symbol, token):
    end = datetime.now(timezone.utc)
    start = end - timedelta(days=60)
    start_str = start.strftime("%Y-%m-%dT%H:%M:%SZ")
    end_str = end.strftime("%Y-%m-%dT%H:%M:%SZ")
    url = (
        f"{BASE_URL}/instruments/{symbol}/bars"
        f"?timeframe=TIME_FRAME_D"
        f"&interval.startTime={start_str}"
        f"&interval.endTime={end_str}"
    )
    req = urllib.request.Request(url, headers={"Authorization": token})
    try:
        with urllib.request.urlopen(req) as resp:
            data = json.loads(resp.read())
        return data.get("bars") or []
    except urllib.error.HTTPError as e:
        print(f"  Warning: HTTP {e.code} for {symbol}", file=sys.stderr)
        return []
    except Exception as e:
        print(f"  Warning: error fetching {symbol}: {e}", file=sys.stderr)
        return []


def compute_volatility(bars):
    closes = [float(b["close"]["value"]) for b in bars]
    if len(closes) < 5:
        return None
    log_returns = [math.log(closes[i] / closes[i - 1]) for i in range(1, len(closes))]
    n = len(log_returns)
    mean = sum(log_returns) / n
    variance = sum((r - mean) ** 2 for r in log_returns) / (n - 1)
    return math.sqrt(variance) * math.sqrt(252)


def main():
    market, n = parse_args()
    [REDACTED_SECRET])

    equities = load_equities(market)
    total = len(equities)
    dprint(f"Fetching bars for {total} {market.upper()} tickers (last 60 days)...")

    results = {}
    with ThreadPoolExecutor(max_workers=5) as executor:
        futures = {
            executor.submit(fetch_bars, eq["ticker"], token): eq
            for eq in equities
        }
        done = 0
        for future in as_completed(futures):
            eq = futures[future]
            bars = future.result()
            done += 1
            if not bars:
                dprint(f"  [{done}/{total}] {eq['ticker']} — skipped (no data)")
                continue
            vol = compute_volatility(bars)
            if vol is None:
                dprint(f"  [{done}/{total}] {eq['ticker']} — skipped (only {len(bars)} bars)")
                continue
            results[eq["ticker"]] = {"name": eq["name"], "vol": vol}
            dprint(f"  [{done}/{total}] {eq['ticker']} — {vol * 100:.1f}%")

    if not results:
        print("No results to display.")
        return

    sorted_results = sorted(results.items(), key=lambda x: x[1]["vol"], reverse=True)
    top = sorted_results[:n]

    market_label = "RU" if market == "ru" else "US"
    print(f"\nTop {n} most volatile {market_label} stocks (annualized volatility, last 60 days):\n")
    header = f"{'#':<5}{'Symbol':<18}{'Name':<35}{'Volatility':>10}"
    print(header)
    print("\u2500" * len(header))
    for i, (symbol, info) in enumerate(top, 1):
        print(f"{i:<5}{symbol:<18}{info['name']:<35}{info['vol'] * 100:>9.1f}%")


if __name__ == "__main__":
    main()


path: references/GRPC.md
content: # Finam Trade API — gRPC (FinamPy)

Библиотека-обёртка [FinamPy](https://github.com/cia76/FinamPy) для работы с [Finam Trade API](https://tradeapi.finam.ru) v2.13.0 из Python по протоколу gRPC.

---

## Установка

```bash
pip install git+https://github.com/cia76/FinamPy.git
```

При первом запуске передать токен явно (сохраняется в системное хранилище):

```python
import os
from FinamPy import FinamPy
fp_provider = FinamPy(os.environ["FINAM_API_KEY"])
```

Последующие вызовы — без токена:

```python
fp_provider = FinamPy()
```

---

## Подключение и базовое использование

```python
from FinamPy import FinamPy

fp_provider = FinamPy()

# Синхронный вызов метода — возвращает ответ или None при ошибке
response = fp_provider.call_function(stub.Method, RequestMessage(...))

# Закрыть канал перед выходом
fp_provider.close_channel()
```

**Свойства провайдера:**

| Свойство | Тип | Описание |
|---|---|---|
| `account_ids` | `list[str]` | Список ID торговых счетов |
| `tz_msk` | `tzinfo` | Часовой пояс МСК (UTC+3) |
| `min_history_date` | `datetime` | Минимальная дата истории баров |

---

## Форматы данных

### Символы

FinamPy использует два формата:

| Формат | Пример | Где используется |
|---|---|---|
| `{board}.{ticker}` (dataname) | `TQBR.SBER`, `SPBFUT.SiH5` | Человекочитаемый ввод |
| `{ticker}@{mic}` (symbol) | `SBER@MISX`, `Si@ROFX` | Все запросы к API |

Конвертация dataname → symbol:

```python
finam_board, ticker = fp_provider.dataname_to_finam_board_ticker('TQBR.SBER')
mic = fp_provider.get_mic(finam_board, ticker)
symbol = f'{ticker}@{mic}'  # → 'SBER@MISX'
```

**MIC-коды основных бирж:**

| MIC | Биржа |
|---|---|
| `MISX` | Московская биржа (акции) |
| `ROFX` | Московская биржа (фьючерсы) |
| `RUSX` | РТС |
| `XNGS` | NASDAQ/NGS |
| `XNYS` | NYSE |

### Таймфреймы

| FinamPy `tf` | Описание | Аналог REST |
|---|---|---|
| `M1` | 1 минута | `TIME_FRAME_M1` |
| `M5` | 5 минут | `TIME_FRAME_M5` |
| `M15` | 15 минут | `TIME_FRAME_M15` |
| `M30` | 30 минут | `TIME_FRAME_M30` |
| `H1` | 1 час | `TIME_FRAME_H1` |
| `H2` | 2 часа | `TIME_FRAME_H2` |
| `H4` | 4 часа | `TIME_FRAME_H4` |
| `D1` | День | `TIME_FRAME_D` |
| `W1` | Неделя | `TIME_FRAME_W` |
| `MN1` | Месяц | `TIME_FRAME_MN` |

Конвертация:

```python
finam_tf, tf_range, intraday = fp_provider.timeframe_to_finam_timeframe('D1')
# finam_tf   — константа для запроса к API
# tf_range   — timedelta максимального диапазона одного запроса
# intraday   — True для внутридневных таймфреймов
```

### Цены и объёмы (Decimal / nanos)

```python
from google.type.decimal_pb2 import Decimal

# Запись цены в запрос
price = Decimal(value='310.50')

# Чтение цены из ответа
price_float = float(response.price.value)

# Чтение денег из поля units + nanos
amount = round(cash.units + cash.nanos * 1e-9, 2)
```

### Временны́е метки (Timestamp)

```python
from datetime import datetime
from google.protobuf.timestamp_pb2 import Timestamp

# datetime → Timestamp
ts = Timestamp(seconds=int(datetime.timestamp(dt)))

# Timestamp → datetime (МСК)
dt = datetime.fromtimestamp(ts.seconds + ts.nanos / 1e9, fp_provider.tz_msk)
```

---

## Сервисы

| Стаб | Модуль | Операции |
|---|---|---|
| `fp_provider.assets_stub` | `assets_service_pb2` | Спецификация тикеров, время сервера |
| `fp_provider.accounts_stub` | `accounts_service_pb2` | Счета, позиции, баланс |
| `fp_provider.orders_stub` | `orders_service_pb2` | Заявки: просмотр, выставление, отмена |
| `fp_provider.marketdata_stub` | `marketdata_service_pb2` | Котировки, бары, стакан, сделки |

---

## AssetsService

### GetAsset — спецификация инструмента

```python
from FinamPy.grpc.assets_service_pb2 import GetAssetRequest, GetAssetResponse

si: GetAssetResponse = fp_provider.call_function(
    fp_provider.assets_stub.GetAsset,
    GetAssetRequest(symbol=symbol, account_id=fp_provider.account_ids[0])
)
```

**Запрос `GetAssetRequest`:**

| Поле | Тип | Описание |
|---|---|---|
| `symbol` | `string` | Инструмент в формате `ticker@mic` |
| `account_id` | `string` | ID торгового счёта |

**Ответ `GetAssetResponse`:**

| Поле | Тип | Описание |
|---|---|---|
| `ticker` | `string` | Тикер |
| `board` | `string` | Режим торгов (TQBR, SPBFUT, ...) |
| `name` | `string` | Полное название |
| `type` | `string` | Тип инструмента |
| `mic` | `string` | MIC-код биржи |
| `lot_size` | `Decimal` | Размер лота |
| `min_step` | `int` | Минимальный шаг цены (в единицах * 10^decimals) |
| `decimals` | `int` | Кол-во знаков после запятой |

Шаг цены в единицах: `si.min_step / (10 ** si.decimals)`

### Clock — время сервера

```python
from FinamPy.grpc.assets_service_pb2 import ClockRequest, ClockResponse

clock: ClockResponse = fp_provider.call_function(
    fp_provider.assets_stub.Clock, ClockRequest()
)
dt_server = datetime.fromtimestamp(clock.timestamp.seconds + clock.timestamp.nanos / 1e9, fp_provider.tz_msk)
```

---

## AccountsService

### GetAccount — информация по счёту

```python
from FinamPy.grpc.accounts_service_pb2 import GetAccountRequest, GetAccountResponse

account: GetAccountResponse = fp_provider.call_function(
    fp_provider.accounts_stub.GetAccount,
    GetAccountRequest(account_id=account_id)
)
if account is None:
    # Счёт не поддерживается в Finam Trade API
    ...
```

**Ответ `GetAccountResponse`:**

| Поле | Тип | Описание |
|---|---|---|
| `positions` | `list` | Открытые позиции (`symbol`, `quantity`, `average_price`, `current_price`) |
| `cash` | `list` | Свободные средства (`units`, `nanos`, `currency_code`) |
| `unrealized_profit` | `Decimal` | Нереализованная прибыль |
| `equity` | `Decimal` | Итого по счёту |

---

## OrdersService

### GetOrders — активные заявки

```python
from FinamPy.grpc.orders_service_pb2 import OrdersRequest, OrdersResponse, ORDER_STATUS_NEW

orders: OrdersResponse = fp_provider.call_function(
    fp_provider.orders_stub.GetOrders,
    OrdersRequest(account_id=account_id)
)
active = [o for o in orders.orders if o.status == ORDER_STATUS_NEW]
```

### PlaceOrder — выставить заявку

```python
from FinamPy.grpc.orders_service_pb2 import Order, OrderState, OrderType, StopCondition
import FinamPy.grpc.side_pb2 as side
from google.type.decimal_pb2 import Decimal

# Рыночная заявка
order_state: OrderState = fp_provider.call_function(
    fp_provider.orders_stub.PlaceOrder,
    Order(
        account_id=account_id,
        symbol=symbol,
        quantity=Decimal(value='10'),
        side=side.SIDE_BUY,
        type=OrderType.ORDER_TYPE_MARKET,
        client_order_id=str(int(datetime.now().timestamp()))
    )
)

# Лимитная заявка
order_state: OrderState = fp_provider.call_function(
    fp_provider.orders_stub.PlaceOrder,
    Order(
        account_id=account_id,
        symbol=symbol,
        quantity=Decimal(value='10'),
        side=side.SIDE_SELL,
        type=OrderType.ORDER_TYPE_LIMIT,
        limit_price=Decimal(value='310.50'),
        client_order_id=str(int(datetime.now().timestamp()))
    )
)

# Стоп заявка
order_state: OrderState = fp_provider.call_function(
    fp_provider.orders_stub.PlaceOrder,
    Order(
        account_id=account_id,
        symbol=symbol,
        quantity=Decimal(value='10'),
        side=side.SIDE_BUY,
        type=OrderType.ORDER_TYPE_STOP,
        stop_price=Decimal(value='315.00'),
        stop_condition=StopCondition.STOP_CONDITION_LAST_UP,
        client_order_id=str(int(datetime.now().timestamp()))
    )
)
```

**Поля `Order`:**

| Поле | Тип | Описание |
|---|---|---|
| `account_id` | `string` | ID торгового счёта |
| `symbol` | `string` | Инструмент `ticker@mic` |
| `quantity` | `Decimal` | Количество (в штуках) |
| `side` | `Side` | `SIDE_BUY` / `SIDE_SELL` |
| `type` | `OrderType` | `ORDER_TYPE_MARKET` / `ORDER_TYPE_LIMIT` / `ORDER_TYPE_STOP` |
| `limit_price` | `Decimal` | Цена для лимитной заявки |
| `stop_price` | `Decimal` | Цена для стоп заявки |
| `stop_condition` | `StopCondition` | `STOP_CONDITION_LAST_UP` / `STOP_CONDITION_LAST_DOWN` |
| `client_order_id` | `string` | Уникальный ID на стороне клиента |

**Ответ `OrderState`:**

| Поле | Тип | Описание |
|---|---|---|
| `order_id` | `string` | ID заявки на бирже |
| `exec_id` | `string` | ID исполнения (для рыночных) |
| `status` | `OrderStatus` | Статус заявки |

### CancelOrder — отменить заявку

```python
from FinamPy.grpc.orders_service_pb2 import CancelOrderRequest

order_state: OrderState = fp_provider.call_function(
    fp_provider.orders_stub.CancelOrder,
    CancelOrderRequest(account_id=account_id, order_id=order_id)
)
```

---

## MarketDataService

### LastQuote — последняя котировка

```python
from FinamPy.grpc.marketdata_service_pb2 import QuoteRequest, QuoteResponse

quote_response: QuoteResponse = fp_provider.call_function(
    fp_provider.marketdata_stub.LastQuote,
    QuoteRequest(symbol=symbol)
)
last_price = float(quote_response.quote.last.value)
```

### Bars — история баров

```python
from google.protobuf.timestamp_pb2 import Timestamp
from google.type.interval_pb2 import Interval
from FinamPy.grpc.marketdata_service_pb2 import BarsRequest, BarsResponse

finam_tf, tf_range, intraday = fp_provider.timeframe_to_finam_timeframe('D1')
start_dt = fp_provider.min_history_date

while start_dt <= datetime.now():
    end_dt = start_dt + tf_range
    bars_response: BarsResponse = fp_provider.call_function(
        fp_provider.marketdata_stub.Bars,
        BarsRequest(
            symbol=symbol,
            timeframe=finam_tf,
            interval=Interval(
                start_time=Timestamp(seconds=int(datetime.timestamp(start_dt))),
                end_time=Timestamp(seconds=int(datetime.timestamp(end_dt)))
            )
        )
    )
    for bar in bars_response.bars:
        dt_bar = datetime.fromtimestamp(bar.timestamp.seconds, fp_provider.tz_msk)
        # bar.open.value, bar.high.value, bar.low.value, bar.close.value, bar.volume.value
    start_dt = end_dt
```

**Поля бара `Bar`:** `timestamp`, `open`, `high`, `low`, `close`, `volume` — все цены типа `Decimal`.

---

## Подписки (стримы)

Подписки запускаются в отдельных потоках. Обработчики регистрируются через `.subscribe()` / `.unsubscribe()`.

### Котировки

```python
from threading import Thread
from FinamPy.grpc.marketdata_service_pb2 import SubscribeQuoteResponse

def on_quote(quote: SubscribeQuoteResponse):
    if quote.quote:
        print(quote.quote[0])

fp_provider.on_quote.subscribe(on_quote)
Thread(target=fp_provider.subscribe_quote_thread, args=((symbol,),)).start()

# Отмена
fp_provider.on_quote.unsubscribe(on_quote)
```

### Стакан

```python
from FinamPy.grpc.marketdata_service_pb2 import SubscribeOrderBookResponse

def on_order_book(ob: SubscribeOrderBookResponse):
    if ob.order_book:
        print(ob.order_book[0])

fp_provider.on_order_book.subscribe(on_order_book)
Thread(target=fp_provider.subscribe_order_book_thread, args=(symbol,)).start()
```

### Сделки (лента)

```python
from FinamPy.grpc.marketdata_service_pb2 import SubscribeLatestTradesResponse

def on_latest_trades(trades: SubscribeLatestTradesResponse):
    if trades.trades:
        print(trades.trades[0])

fp_provider.on_latest_trades.subscribe(on_latest_trades)
Thread(target=fp_provider.subscribe_latest_trades_thread, args=(symbol,)).start()
```

### Бары в реальном времени

```python
from FinamPy.grpc.marketdata_service_pb2 import TimeFrame, SubscribeBarsResponse, Bar

last_bar = None
dt_last_bar = None

def on_new_bar(bars: SubscribeBarsResponse, finam_timeframe: TimeFrame.ValueType):
    global last_bar, dt_last_bar
    for bar in bars.bars:
        dt_bar = datetime.fromtimestamp(bar.timestamp.seconds, fp_provider.tz_msk)
        if dt_last_bar is not None and dt_last_bar < dt_bar:
            # Закрылся предыдущий бар — можно обрабатывать last_bar
            print(f'{dt_last_bar} O:{last_bar.open.value} C:{last_bar.close.value}')
        last_bar = bar
        dt_last_bar = dt_bar

finam_tf, _, _ = fp_provider.timeframe_to_finam_timeframe('M1')
fp_provider.on_new_bar.subscribe(on_new_bar)
Thread(target=fp_provider.subscribe_bars_thread, args=(symbol, finam_tf)).start()
```

### Свои заявки и сделки

```python
fp_provider.on_order.subscribe(lambda order: print(f'Заявка: {order}'))
fp_provider.on_trade.subscribe(lambda trade: print(f'Сделка: {trade}'))
Thread(target=fp_provider.subscribe_orders_thread).start()
Thread(target=fp_provider.subscribe_trades_thread).start()
```

---

## Вспомогательные методы

| Метод | Возвращает | Описание |
|---|---|---|
| `dataname_to_finam_board_ticker(dataname)` | `(board, ticker)` | Разбивает `TQBR.SBER` на board и ticker |
| `get_mic(board, ticker)` | `str` | MIC-код биржи по board и ticker |
| `get_symbol_info(ticker, mic)` | `GetAssetResponse` | Спецификация инструмента |
| `timeframe_to_finam_timeframe(tf)` | `(finam_tf, tf_range, intraday)` | Конвертация строкового TF в константу API |
| `finam_timeframe_to_timeframe(finam_tf)` | `(tf, tf_range, intraday)` | Обратная конвертация |
| `call_function(stub_method, request)` | `Response \| None` | Синхронный вызов gRPC-метода |
| `close_channel()` | — | Закрыть gRPC-канал перед выходом |

---

## Обработка ошибок

`call_function` возвращает `None` при любой ошибке (недоступность, таймаут, неверные параметры). Всегда проверяйте результат:

```python
result = fp_provider.call_function(stub.Method, request)
if result is None:
    # Логировать или обработать ошибку
    ...
```

Ошибки и подробности логируются самой библиотекой в стандартный `logging`.

**Типичные причины `None`:**
- Истёк токен (библиотека переподключается автоматически, но первый запрос может упасть)
- Неверный `symbol` или `account_id`
- Счёт не поддерживается в Finam Trade API
- Превышен лимит запросов

---

## Особенности

- Из спецификации фьючерса невозможно определить, является ли он фьючерсом на сырьё
- Для дневных и выше таймфреймов (`intraday=False`) следует обрезать время у `datetime`: `dt_bar.date()`
- `subscribe_quote_thread` принимает **кортеж** символов: `args=((symbol,),)` — двойные скобки обязательны
- `client_order_id` должен быть уникальным; удобно использовать `str(int(datetime.now().timestamp()))`

path: references/REST.md
content: # Finam Trade-API REST

API для торговых операций

**Server:** https://api.finam.ru

---

## AccountsService

- [GET /v1/accounts/{accountId}](#получение-информации-по-конкретному-аккаунту)
- [GET /v1/accounts/{accountId}/trades](#получение-истории-по-сделкам-аккаунта)
- [GET /v1/accounts/{accountId}/transactions](#получение-списка-транзакций-аккаунта)

---

### Получение информации по конкретному аккаунту

`GET /v1/accounts/{accountId}`

**Path Parameters**

| Name | Type | Required | Description |
| --- | --- | --- | --- |
| accountId | string | yes | Идентификатор аккаунта |

**Responses**

| Code | Description |
| --- | --- |
| 200 | Успешный ответ |
| 401 | Срок действия токена истек или токен недействителен |
| 404 | Счёт не был найден в токене |
| 429 | Слишком много запросов. Доступный лимит - 200 запросов в минуту |
| 500 | Внутренняя ошибка сервиса. Попробуйте позже |
| 503 | Сервис на данный момент не доступен. Попробуйте позже |
| 504 | Крайний срок истек до завершения операции |
| default | Непредвиденная ошибка |

**Request Example**

```curl
curl 'https://api.finam.ru/v1/accounts/{accountId}' \
  --header '[REDACTED_SECRET]'
```

**Response Schema (200)**

```json
{
  "accountId": "string",
  "type": "string",
  "status": "string",
  "equity": {
    "value": "string"
  },
  "unrealizedProfit": {
    "value": "string"
  },
  "positions": [
    {
      "symbol": "string",
      "quantity": {
        "value": "string"
      },
      "averagePrice": {
        "value": "string"
      },
      "currentPrice": {
        "value": "string"
      },
      "maintenanceMargin": {
        "value": "string"
      },
      "dailyPnl": {
        "value": "string"
      },
      "unrealizedPnl": {
        "value": "string"
      }
    }
  ],
  "cash": [
    {
      "currencyCode": "string",
      "units": "string",
      "nanos": 1
    }
  ],
  "portfolioMc": {
    "availableCash": {
      "value": "string"
    },
    "initialMargin": {
      "value": "string"
    },
    "maintenanceMargin": {
      "value": "string"
    }
  },
  "portfolioMct": {},
  "portfolioForts": {
    "availableCash": {
      "value": "string"
    },
    "moneyReserved": {
      "value": "string"
    }
  },
  "openAccountDate": "2026-03-27T13:09:52.291Z",
  "firstTradeDate": "2026-03-27T13:09:52.291Z",
  "firstNonTradeDate": "2026-03-27T13:09:52.291Z"
}
```

---

### Получение истории по сделкам аккаунта

`GET /v1/accounts/{accountId}/trades`

Параметры:
- `accountId` — передается в URL пути
- `limit` и `interval` — передаются как query-параметры

**Path Parameters**

| Name | Type | Required | Description |
| --- | --- | --- | --- |
| accountId | string | yes | Идентификатор аккаунта |

**Query Parameters**

| Name | Type | Required | Description |
| --- | --- | --- | --- |
| limit | integer (int32) | no | Лимит количества сделок |
| interval.start_time | string (date-time) | no | Inclusive start of the interval. If specified, a Timestamp matching this interval will have to be the same or after the start. |
| interval.end_time | string (date-time) | no | Exclusive end of the interval. If specified, a Timestamp matching this interval will have to be before the end. |

**Responses**

| Code | Description |
| --- | --- |
| 200 | Успешный ответ |
| 400 | Неверно передан интервал |
| 401 | Срок действия токена истек или токен недействителен |
| 404 | Счёт не был найден в токене |
| 429 | Слишком много запросов. Доступный лимит - 200 запросов в минуту |
| 500 | Внутренняя ошибка сервиса. Попробуйте позже |
| 503 | Сервис на данный момент не доступен. Попробуйте позже |
| 504 | Крайний срок истек до завершения операции |
| default | Непредвиденная ошибка |

**Request Example**

```curl
curl 'https://api.finam.ru/v1/accounts/{accountId}/trades?limit=1&interval.start_time=&interval.end_time=' \
  --header '[REDACTED_SECRET]'
```

**Response Schema (200)**

```json
{
  "trades": [
    {
      "tradeId": "string",
      "symbol": "string",
      "price": {
        "value": "string"
      },
      "size": {
        "value": "string"
      },
      "side": "SIDE_UNSPECIFIED",
      "timestamp": "2026-03-27T13:09:52.291Z",
      "orderId": "string",
      "accountId": "string",
      "comment": "string"
    }
  ]
}
```

---

### Получение списка транзакций аккаунта

`GET /v1/accounts/{accountId}/transactions`

Параметры:
- `accountId` — передается в URL пути
- `limit` и `interval` — передаются как query-параметры

**Path Parameters**

| Name | Type | Required | Description |
| --- | --- | --- | --- |
| accountId | string | yes | Идентификатор аккаунта |

**Query Parameters**

| Name | Type | Required | Description |
| --- | --- | --- | --- |
| limit | integer (int32) | no | Лимит количества транзакций |
| interval.start_time | string (date-time) | no | Inclusive start of the interval. If specified, a Timestamp matching this interval will have to be the same or after the start. |
| interval.end_time | string (date-time) | no | Exclusive end of the interval. If specified, a Timestamp matching this interval will have to be before the end. |

**Responses**

| Code | Description |
| --- | --- |
| 200 | Успешный ответ |
| 400 | Неверно передан интервал |
| 401 | Срок действия токена истек или токен недействителен |
| 404 | Счёт не был найден в токене |
| 429 | Слишком много запросов. Доступный лимит - 200 запросов в минуту |
| 500 | Внутренняя ошибка сервиса. Попробуйте позже |
| 503 | Сервис на данный момент не доступен. Попробуйте позже |
| 504 | Крайний срок истек до завершения операции |
| default | Непредвиденная ошибка |

**Request Example**

```curl
curl 'https://api.finam.ru/v1/accounts/{accountId}/transactions?limit=1&interval.start_time=&interval.end_time=' \
  --header '[REDACTED_SECRET]'
```

**Response Schema (200)**

```json
{
  "transactions": [
    {
      "id": "string",
      "category": "string",
      "timestamp": "2026-03-27T13:09:52.291Z",
      "symbol": "string",
      "change": {
        "currencyCode": "string",
        "units": "string",
        "nanos": 1
      },
      "trade": {
        "size": {
          "value": "string"
        },
        "price": {
          "value": "string"
        },
        "accruedInterest": {
          "value": "string"
        }
      },
      "transactionCategory": "OTHERS",
      "transactionName": "string",
      "changeQty": {
        "value": "string"
      }
    }
  ]
}
```

---

## AssetsService

- [GET /v1/assets](#получение-списка-доступных-инструментов-их-описание)
- [GET /v1/assets/all](#получение-списка-всех-инструментов-их-описание)
- [GET /v1/assets/clock](#получение-времени-на-сервере)
- [GET /v1/assets/{symbol}](#получение-информации-по-конкретному-инструменту)
- [GET /v1/assets/{symbol}/params](#получение-торговых-параметров-по-инструменту)
- [GET /v1/assets/{symbol}/schedule](#получение-расписания-торгов-для-инструмента)
- [GET /v1/assets/{underlyingSymbol}/options](#получение-цепочки-опционов-для-базового-актива)
- [GET /v1/exchanges](#получение-списка-доступных-бирж-названия-и-mic-коды)

---

### Получение списка доступных инструментов, их описание

`GET /v1/assets`

**Responses**

| Code | Description |
| --- | --- |
| 200 | Успешный ответ |
| 401 | Срок действия токена истек или токен недействителен |
| 404 | Счёт не был найден в токене |
| 429 | Слишком много запросов. Доступный лимит - 200 запросов в минуту |
| 500 | Внутренняя ошибка сервиса. Попробуйте позже |
| 503 | Сервис на данный момент не доступен. Попробуйте позже |
| 504 | Крайний срок истек до завершения операции |
| default | Непредвиденная ошибка |

**Request Example**

```curl
curl https://api.finam.ru/v1/assets \
  --header '[REDACTED_SECRET]'
```

**Response Schema (200)**

```json
{
  "assets": [
    {
      "symbol": "string",
      "id": "string",
      "ticker": "string",
      "mic": "string",
      "isin": "string",
      "type": "string",
      "name": "string",
      "isArchived": true
    }
  ]
}
```

---

### Получение списка всех инструментов, их описание

`GET /v1/assets/all`

**Query Parameters**

| Name | Type | Required | Description |
| --- | --- | --- | --- |
| cursor | string (int64) | no | Курсор для пагинации. Указывает sec_id инструмента, с которого должен начинаться список. Для первого запроса оставьте поле пустым (значение 0). Для последующих запросов используйте значение next_cursor из предыдущего ответа. |
| only_active | boolean | no | Фильтрация по статусу инструмента: выбираются только активные (неархивные) инструменты. По умолчанию: false. |
| only_disabled | boolean | no | Фильтрация по статусу инструмента: выбираются только неактивные (архивные) инструменты. По умолчанию: false. |

**Responses**

| Code | Description |
| --- | --- |
| 200 | Успешный ответ |
| 401 | Срок действия токена истек или токен недействителен |
| 404 | Счёт не был найден в токене |
| 429 | Слишком много запросов. Доступный лимит - 200 запросов в минуту |
| 500 | Внутренняя ошибка сервиса. Попробуйте позже |
| 503 | Сервис на данный момент не доступен. Попробуйте позже |
| 504 | Крайний срок истек до завершения операции |
| default | Непредвиденная ошибка |

**Request Example**

```curl
curl 'https://api.finam.ru/v1/assets/all?cursor=&only_active=true&only_disabled=true' \
  --header '[REDACTED_SECRET]'
```

**Response Schema (200)**

```json
{
  "assets": [
    {
      "symbol": "string",
      "id": "string",
      "ticker": "string",
      "mic": "string",
      "isin": "string",
      "type": "string",
      "name": "string",
      "isArchived": true
    }
  ],
  "nextCursor": "string"
}
```

---

### Получение времени на сервере

`GET /v1/assets/clock`

**Responses**

| Code | Description |
| --- | --- |
| 200 | Успешный ответ |
| 401 | Срок действия токена истек или токен недействителен |
| 404 | Счёт не был найден в токене |
| 429 | Слишком много запросов. Доступный лимит - 200 запросов в минуту |
| 500 | Внутренняя ошибка сервиса. Попробуйте позже |
| 503 | Сервис на данный момент не доступен. Попробуйте позже |
| 504 | Крайний срок истек до завершения операции |
| default | Непредвиденная ошибка |

**Request Example**

```curl
curl https://api.finam.ru/v1/assets/clock \
  --header '[REDACTED_SECRET]'
```

**Response Schema (200)**

```json
{
  "timestamp": "2026-03-27T13:09:52.291Z"
}
```

---

### Получение информации по конкретному инструменту

`GET /v1/assets/{symbol}`

Параметры:
- `symbol` — передается в URL пути
- `account_id` — передаётся как query-параметр

**Path Parameters**

| Name | Type | Required | Description |
| --- | --- | --- | --- |
| symbol | string | yes | Символ инструмента |

**Query Parameters**

| Name | Type | Required | Description |
| --- | --- | --- | --- |
| account_id | string | no | ID аккаунта для которого будет подбираться информация по инструменту |

**Responses**

| Code | Description |
| --- | --- |
| 200 | Успешный ответ |
| 400 | Неверно передан символ или счет. Символ должен быть в виде ticker@mic. Где ticker — это, например, SBER. А mic, например, MISX |
| 401 | Срок действия токена истек или токен недействителен |
| 404 | Счёт не был найден в токене |
| 429 | Слишком много запросов. Доступный лимит - 200 запросов в минуту |
| 500 | Внутренняя ошибка сервиса. Попробуйте позже |
| 503 | Сервис на данный момент не доступен. Попробуйте позже |
| 504 | Крайний срок истек до завершения операции |
| default | Непредвиденная ошибка |

**Request Example**

```curl
curl 'https://api.finam.ru/v1/assets/{symbol}?account_id=' \
  --header '[REDACTED_SECRET]'
```

**Response Schema (200)**

```json
{
  "board": "string",
  "id": "string",
  "ticker": "string",
  "mic": "string",
  "isin": "string",
  "type": "string",
  "name": "string",
  "decimals": 1,
  "minStep": "string",
  "lotSize": {
    "value": "string"
  },
  "expirationDate": {
    "year": 1,
    "month": 1,
    "day": 1
  },
  "quoteCurrency": "string"
}
```

---

### Получение торговых параметров по инструменту

`GET /v1/assets/{symbol}/params`

Параметры:
- `symbol` — передается в URL пути
- `account_id` — передаётся как query-параметр

**Path Parameters**

| Name | Type | Required | Description |
| --- | --- | --- | --- |
| symbol | string | yes | Символ инструмента |

**Query Parameters**

| Name | Type | Required | Description |
| --- | --- | --- | --- |
| account_id | string | no | ID аккаунта для которого будут подбираться торговые параметры |

**Responses**

| Code | Description |
| --- | --- |
| 200 | Успешный ответ |
| 400 | Неверно передан символ или счет. Символ должен быть в виде ticker@mic. Где ticker — это, например, SBER. А mic, например, MISX |
| 401 | Срок действия токена истек или токен недействителен |
| 404 | Счёт не был найден в токене |
| 429 | Слишком много запросов. Доступный лимит - 200 запросов в минуту |
| 500 | Внутренняя ошибка сервиса. Попробуйте позже |
| 503 | Сервис на данный момент не доступен. Попробуйте позже |
| 504 | Крайний срок истек до завершения операции |
| default | Непредвиденная ошибка |

**Request Example**

```curl
curl 'https://api.finam.ru/v1/assets/{symbol}/params?account_id=' \
  --header '[REDACTED_SECRET]'
```

**Response Schema (200)**

```json
{
  "symbol": "string",
  "accountId": "string",
  "tradeable": true,
  "longable": {
    "value": "NOT_AVAILABLE",
    "haltedDays": 1
  },
  "shortable": {
    "value": "NOT_AVAILABLE",
    "haltedDays": 1
  },
  "longRiskRate": {
    "value": "string"
  },
  "longCollateral": {
    "currencyCode": "string",
    "units": "string",
    "nanos": 1
  },
  "shortRiskRate": {
    "value": "string"
  },
  "shortCollateral": {
    "currencyCode": "string",
    "units": "string",
    "nanos": 1
  },
  "longInitialMargin": {
    "currencyCode": "string",
    "units": "string",
    "nanos": 1
  },
  "shortInitialMargin": {
    "currencyCode": "string",
    "units": "string",
    "nanos": 1
  },
  "isTradable": true,
  "priceType": "UNKNOWN"
}
```

---

### Получение расписания торгов для инструмента

`GET /v1/assets/{symbol}/schedule`

**Path Parameters**

| Name | Type | Required | Description |
| --- | --- | --- | --- |
| symbol | string | yes | Символ инструмента |

**Responses**

| Code | Description |
| --- | --- |
| 200 | Успешный ответ |
| 400 | Неверно передан символ. Символ должен быть в виде ticker@mic. Где ticker — это, например, SBER. А mic, например, MISX |
| 401 | Срок действия токена истек или токен недействителен |
| 404 | Счёт не был найден в токене |
| 429 | Слишком много запросов. Доступный лимит - 200 запросов в минуту |
| 500 | Внутренняя ошибка сервиса. Попробуйте позже |
| 503 | Сервис на данный момент не доступен. Попробуйте позже |
| 504 | Крайний срок истек до завершения операции |
| default | Непредвиденная ошибка |

**Request Example**

```curl
curl 'https://api.finam.ru/v1/assets/{symbol}/schedule' \
  --header '[REDACTED_SECRET]'
```

**Response Schema (200)**

```json
{
  "symbol": "string",
  "sessions": [
    {
      "type": "string",
      "interval": {
        "startTime": "2026-03-27T13:09:52.291Z",
        "endTime": "2026-03-27T13:09:52.291Z"
      }
    }
  ]
}
```

---

### Получение цепочки опционов для базового актива

`GET /v1/assets/{underlyingSymbol}/options`

**Path Parameters**

| Name | Type | Required | Description |
| --- | --- | --- | --- |
| underlyingSymbol | string | yes | Символ базового актива опциона |

**Query Parameters**

| Name | Type | Required | Description |
| --- | --- | --- | --- |
| root | string | no | Опциональный параметр. Актуален для опционов на фьючерсы, по типу (недельные, месячные). Если параметр не указан, будут возвращены опционы с ближайшей датой экспирации. |
| expirationDate.year | integer (int32) | no | Year of the date. Must be from 1 to 9999, or 0 to specify a date without a year. |
| expirationDate.month | integer (int32) | no | Month of a year. Must be from 1 to 12, or 0 to specify a year without a month and day. |
| expirationDate.day | integer (int32) | no | Day of a month. Must be from 1 to 31 and valid for the year and month, or 0 to specify a year by itself or a year and month where the day isn't significant. |

**Responses**

| Code | Description |
| --- | --- |
| 200 | Успешный ответ |
| 400 | Неверно передан символ. Символ должен быть в виде ticker@mic. Где ticker — это, например, SBER. А mic, например, MISX |
| 401 | Срок действия токена истек или токен недействителен |
| 404 | Счёт не был найден в токене |
| 429 | Слишком много запросов. Доступный лимит - 200 запросов в минуту |
| 500 | Внутренняя ошибка сервиса. Попробуйте позже |
| 503 | Сервис на данный момент не доступен. Попробуйте позже |
| 504 | Крайний срок истек до завершения операции |
| default | Непредвиденная ошибка |

**Request Example**

```curl
curl 'https://api.finam.ru/v1/assets/{underlyingSymbol}/options?root=&expirationDate.year=1&expirationDate.month=1&expirationDate.day=1' \
  --header '[REDACTED_SECRET]'
```

**Response Schema (200)**

```json
{
  "symbol": "string",
  "options": [
    {
      "symbol": "string",
      "type": "TYPE_UNSPECIFIED",
      "contractSize": {
        "value": "string"
      },
      "tradeFirstDay": {
        "year": 1,
        "month": 1,
        "day": 1
      },
      "tradeLastDay": {
        "year": 1,
        "month": 1,
        "day": 1
      },
      "strike": {
        "value": "string"
      },
      "multiplier": {
        "value": "string"
      },
      "expirationFirstDay": {
        "year": 1,
        "month": 1,
        "day": 1
      },
      "expirationLastDay": {
        "year": 1,
        "month": 1,
        "day": 1
      }
    }
  ]
}
```

---

### Получение списка доступных бирж, названия и mic коды

`GET /v1/exchanges`

**Responses**

| Code | Description |
| --- | --- |
| 200 | Успешный ответ |
| 401 | Срок действия токена истек или токен недействителен |
| 404 | Счёт не был найден в токене |
| 429 | Слишком много запросов. Доступный лимит - 200 запросов в минуту |
| 500 | Внутренняя ошибка сервиса. Попробуйте позже |
| 503 | Сервис на данный момент не доступен. Попробуйте позже |
| 504 | Крайний срок истек до завершения операции |
| default | Непредвиденная ошибка |

**Request Example**

```curl
curl https://api.finam.ru/v1/exchanges \
  --header '[REDACTED_SECRET]'
```

**Response Schema (200)**

```json
{
  "exchanges": [
    {
      "mic": "string",
      "name": "string"
    }
  ]
}
```

---

## AuthService

- [POST /v1/sessions](#получение-jwt-токена-из-api-токена)
- [POST /v1/sessions/details](#получение-информации-о-токене-сессии)

---

### Получение JWT токена из API токена

`POST /v1/sessions`

Все поля передаются в теле запроса.

**Body** (required, application/json)

| Name | Type | Required | Description |
| --- | --- | --- | --- |
| secret | string | yes | API токен (secret key) |

**Responses**

| Code | Description |
| --- | --- |
| 200 | Успешный ответ |
| 401 | Срок действия токена истек или токен недействителен |
| 404 | Счёт не был найден в токене |
| 429 | Слишком много запросов. Доступный лимит - 200 запросов в минуту |
| 500 | Внутренняя ошибка сервиса. Попробуйте позже |
| 503 | Сервис на данный момент не доступен. Попробуйте позже |
| 504 | Крайний срок истек до завершения операции |
| default | Непредвиденная ошибка |

**Request Example**

```curl
curl https://api.finam.ru/v1/sessions \
  --request POST \
  --header 'Content-Type: application/json' \
  --header '[REDACTED_SECRET]' \
  --data '{
  "secret": ""
}'
```

**Response Schema (200)**

```json
{
  "token": "string"
}
```

---

### Получение информации о токене сессии

`POST /v1/sessions/details`

Токен передается в теле запроса для безопасности. Получение информации о токене. Также включает список доступных счетов.

**Body** (required, application/json)

| Name | Type | Required | Description |
| --- | --- | --- | --- |
| token | string | yes | JWT-токен |

**Responses**

| Code | Description |
| --- | --- |
| 200 | Успешный ответ |
| 401 | Срок действия токена истек или токен недействителен |
| 404 | Счёт не был найден в токене |
| 429 | Слишком много запросов. Доступный лимит - 200 запросов в минуту |
| 500 | Внутренняя ошибка сервиса. Попробуйте позже |
| 503 | Сервис на данный момент не доступен. Попробуйте позже |
| 504 | Крайний срок истек до завершения операции |
| default | Непредвиденная ошибка |

**Request Example**

```curl
curl https://api.finam.ru/v1/sessions/details \
  --request POST \
  --header 'Content-Type: application/json' \
  --header '[REDACTED_SECRET]' \
  --data '{
  "token": ""
}'
```

**Response Schema (200)**

```json
{
  "createdAt": "2026-03-27T13:09:52.291Z",
  "expiresAt": "2026-03-27T13:09:52.291Z",
  "mdPermissions": [
    {
      "quoteLevel": "QUOTE_LEVEL_UNSPECIFIED",
      "delayMinutes": 1,
      "mic": "string",
      "country": "string",
      "continent": "string",
      "worldwide": true
    }
  ],
  "accountIds": [
    "string"
  ],
  "readonly": true
}
```

---

## MarketDataService

- [GET /v1/instruments/{symbol}/bars](#получение-исторических-данных-по-инструменту-агрегированные-свечи)
- [GET /v1/instruments/{symbol}/orderbook](#получение-текущего-стакана-по-инструменту)
- [GET /v1/instruments/{symbol}/quotes/latest](#получение-последней-котировки-по-инструменту)
- [GET /v1/instruments/{symbol}/trades/latest](#получение-списка-последних-сделок-по-инструменту)

---

### Получение исторических данных по инструменту (агрегированные свечи)

`GET /v1/instruments/{symbol}/bars`

Параметры:
- `symbol` — передается в URL пути
- `timeframe` и `interval` — передаются как query-параметры

**Path Parameters**

| Name | Type | Required | Description |
| --- | --- | --- | --- |
| symbol | string | yes | Символ инструмента |

**Query Parameters**

| Name | Type | Required | Description |
| --- | --- | --- | --- |
| timeframe | string (enum) | no | Необходимый таймфрейм. Значения: `TIME_FRAME_UNSPECIFIED` (не указан), `TIME_FRAME_M1` (1 мин, глубина 7 дней), `TIME_FRAME_M5` (5 мин, 30 дней), `TIME_FRAME_M15` (15 мин, 30 дней), `TIME_FRAME_M30` (30 мин, 30 дней), `TIME_FRAME_H1` (1 час, 30 дней), `TIME_FRAME_H2` (2 часа, 30 дней), `TIME_FRAME_H4` (4 часа, 30 дней), `TIME_FRAME_H8` (8 часов, 30 дней), `TIME_FRAME_D` (день, 365 дней), `TIME_FRAME_W` (неделя, 365*5 дней), `TIME_FRAME_MN` (месяц, 365*5 дней), `TIME_FRAME_QR` (квартал, 365*5 дней) |
| interval.start_time | string (date-time) | no | Inclusive start of the interval. If specified, a Timestamp matching this interval will have to be the same or after the start. |
| interval.end_time | string (date-time) | no | Exclusive end of the interval. If specified, a Timestamp matching this interval will have to be before the end. |

**Responses**

| Code | Description |
| --- | --- |
| 200 | Успешный ответ |
| 400 | Неверно передан символ или интервал. Символ должен быть в виде ticker@mic. Где ticker — это, например, SBER. А mic, например, MISX |
| 401 | Срок действия токена истек или токен недействителен |
| 404 | Счёт не был найден в токене |
| 429 | Слишком много запросов. Доступный лимит - 200 запросов в минуту |
| 500 | Внутренняя ошибка сервиса. Попробуйте позже |
| 503 | Сервис на данный момент не доступен. Попробуйте позже |
| 504 | Крайний срок истек до завершения операции |
| default | Непредвиденная ошибка |

**Request Example**

```curl
curl 'https://api.finam.ru/v1/instruments/{symbol}/bars?timeframe=TIME_FRAME_UNSPECIFIED&interval.start_time=&interval.end_time=' \
  --header '[REDACTED_SECRET]'
```

**Response Schema (200)**

```json
{
  "symbol": "string",
  "bars": [
    {
      "timestamp": "2026-03-27T13:09:52.291Z",
      "open": {
        "value": "string"
      },
      "high": {
        "value": "string"
      },
      "low": {
        "value": "string"
      },
      "close": {
        "value": "string"
      },
      "volume": {
        "value": "string"
      }
    }
  ]
}
```

---

### Получение текущего стакана по инструменту

`GET /v1/instruments/{symbol}/orderbook`

**Path Parameters**

| Name | Type | Required | Description |
| --- | --- | --- | --- |
| symbol | string | yes | Символ инструмента |

**Responses**

| Code | Description |
| --- | --- |
| 200 | Успешный ответ |
| 401 | Срок действия токена истек или токен недействителен |
| 404 | Счёт не был найден в токене |
| 429 | Слишком много запросов. Доступный лимит - 200 запросов в минуту |
| 500 | Внутренняя ошибка сервиса. Попробуйте позже |
| 503 | Сервис на данный момент не доступен. Попробуйте позже |
| 504 | Крайний срок истек до завершения операции |
| default | Непредвиденная ошибка |

**Request Example**

```curl
curl 'https://api.finam.ru/v1/instruments/{symbol}/orderbook' \
  --header '[REDACTED_SECRET]'
```

**Response Schema (200)**

```json
{
  "symbol": "string",
  "orderbook": {
    "rows": [
      {
        "price": {
          "value": "string"
        },
        "sellSize": {
          "value": "string"
        },
        "buySize": {
          "value": "string"
        },
        "action": "ACTION_UNSPECIFIED",
        "mpid": "string",
        "timestamp": "2026-03-27T13:09:52.291Z"
      }
    ]
  }
}
```

---

### Получение последней котировки по инструменту

`GET /v1/instruments/{symbol}/quotes/latest`

**Path Parameters**

| Name | Type | Required | Description |
| --- | --- | --- | --- |
| symbol | string | yes | Символ инструмента |

**Responses**

| Code | Description |
| --- | --- |
| 200 | Успешный ответ |
| 401 | Срок действия токена истек или токен недействителен |
| 404 | Счёт не был найден в токене |
| 429 | Слишком много запросов. Доступный лимит - 200 запросов в минуту |
| 500 | Внутренняя ошибка сервиса. Попробуйте позже |
| 503 | Сервис на данный момент не доступен. Попробуйте позже |
| 504 | Крайний срок истек до завершения операции |
| default | Непредвиденная ошибка |

**Request Example**

```curl
curl 'https://api.finam.ru/v1/instruments/{symbol}/quotes/latest' \
  --header '[REDACTED_SECRET]'
```

**Response Schema (200)**

```json
{
  "symbol": "string",
  "quote": {
    "symbol": "string",
    "timestamp": "2026-03-27T13:09:52.291Z",
    "ask": {
      "value": "string"
    },
    "askSize": {
      "value": "string"
    },
    "bid": {
      "value": "string"
    },
    "bidSize": {
      "value": "string"
    },
    "last": {
      "value": "string"
    },
    "lastSize": {
      "value": "string"
    },
    "volume": {
      "value": "string"
    },
    "turnover": {
      "value": "string"
    },
    "open": {
      "value": "string"
    },
    "high": {
      "value": "string"
    },
    "low": {
      "value": "string"
    },
    "close": {
      "value": "string"
    },
    "change": {
      "value": "string"
    },
    "option": {
      "openInterest": {
        "value": "string"
      },
      "impliedVolatility": {
        "value": "string"
      },
      "theoreticalPrice": {
        "value": "string"
      },
      "delta": {
        "value": "string"
      },
      "gamma": {
        "value": "string"
      },
      "theta": {
        "value": "string"
      },
      "vega": {
        "value": "string"
      },
      "rho": {
        "value": "string"
      }
    }
  }
}
```

---

### Получение списка последних сделок по инструменту

`GET /v1/instruments/{symbol}/trades/latest`

**Path Parameters**

| Name | Type | Required | Description |
| --- | --- | --- | --- |
| symbol | string | yes | Символ инструмента |

**Responses**

| Code | Description |
| --- | --- |
| 200 | Успешный ответ |
| 401 | Срок действия токена истек или токен недействителен |
| 404 | Счёт не был найден в токене |
| 429 | Слишком много запросов. Доступный лимит - 200 запросов в минуту |
| 500 | Внутренняя ошибка сервиса. Попробуйте позже |
| 503 | Сервис на данный момент не доступен. Попробуйте позже |
| 504 | Крайний срок истек до завершения операции |
| default | Непредвиденная ошибка |

**Request Example**

```curl
curl 'https://api.finam.ru/v1/instruments/{symbol}/trades/latest' \
  --header '[REDACTED_SECRET]'
```

**Response Schema (200)**

```json
{
  "symbol": "string",
  "trades": [
    {
      "tradeId": "string",
      "mpid": "string",
      "timestamp": "2026-03-27T13:09:52.291Z",
      "price": {
        "value": "string"
      },
      "size": {
        "value": "string"
      },
      "side": "SIDE_UNSPECIFIED"
    }
  ]
}
```

---

## UsageMetricsService

- [GET /v1/usage](#получение-текущих-метрик-использования-для-пользователя)

---

### Получение текущих метрик использования для пользователя

`GET /v1/usage`

**Responses**

| Code | Description |
| --- | --- |
| 200 | Успешный ответ |
| 401 | Срок действия токена истек или токен недействителен |
| 404 | Счёт не был найден в токене |
| 429 | Слишком много запросов. Доступный лимит - 200 запросов в минуту |
| 500 | Внутренняя ошибка сервиса. Попробуйте позже |
| 503 | Сервис на данный момент не доступен. Попробуйте позже |
| 504 | Крайний срок истек до завершения операции |
| default | Непредвиденная ошибка |

**Request Example**

```curl
curl https://api.finam.ru/v1/usage \
  --header '[REDACTED_SECRET]'
```

**Response Schema (200)**

```json
{
  "quotas": [
    {
      "name": "string",
      "limit": "string",
      "remaining": "string",
      "resetTime": "2026-03-27T13:09:52.291Z"
    }
  ]
}
```

---

## OrdersService

- [GET /v1/accounts/{accountId}/orders](#получение-списка-заявок-для-аккаунта)
- [POST /v1/accounts/{accountId}/orders](#выставление-биржевой-заявки)
- [GET /v1/accounts/{accountId}/orders/{orderId}](#получение-информации-о-конкретном-ордере)
- [DELETE /v1/accounts/{accountId}/orders/{orderId}](#отмена-биржевой-заявки)
- [POST /v1/accounts/{accountId}/sltp-orders](#выставление-sltp-заявки)

---

### Получение списка заявок для аккаунта

`GET /v1/accounts/{accountId}/orders`

**Path Parameters**

| Name | Type | Required | Description |
| --- | --- | --- | --- |
| accountId | string | yes | Идентификатор аккаунта |

**Responses**

| Code | Description |
| --- | --- |
| 200 | Успешный ответ |
| 401 | Срок действия токена истек или токен недействителен |
| 404 | Счёт не был найден |
| 429 | Слишком много запросов. Доступный лимит - 200 запросов в минуту |
| 500 | Внутренняя ошибка сервиса. Попробуйте позже |
| 503 | Сервис на данный момент не доступен. Попробуйте позже |
| 504 | Крайний срок истек до завершения операции |
| default | Непредвиденная ошибка |

**Request Example**

```curl
curl 'https://api.finam.ru/v1/accounts/{accountId}/orders' \
  --header '[REDACTED_SECRET]'
```

**Response Schema (200)**

```json
{
  "orders": [
    {
      "orderId": "string",
      "execId": "string",
      "status": "ORDER_STATUS_UNSPECIFIED",
      "order": {
        "accountId": "string",
        "symbol": "string",
        "quantity": {
          "value": "string"
        },
        "side": "SIDE_UNSPECIFIED",
        "type": "ORDER_TYPE_UNSPECIFIED",
        "timeInForce": "TIME_IN_FORCE_UNSPECIFIED",
        "limitPrice": {
          "value": "string"
        },
        "stopPrice": {
          "value": "string"
        },
        "stopCondition": "STOP_CONDITION_UNSPECIFIED",
        "legs": [
          {
            "symbol": "string",
            "quantity": {
              "value": "string"
            },
            "side": "SIDE_UNSPECIFIED"
          }
        ],
        "clientOrderId": "string",
        "validBefore": "VALID_BEFORE_UNSPECIFIED",
        "comment": "string"
      },
      "transactAt": "2026-03-27T13:09:52.291Z",
      "acceptAt": "2026-03-27T13:09:52.291Z",
      "withdrawAt": "2026-03-27T13:09:52.291Z",
      "initialQuantity": {
        "value": "string"
      },
      "executedQuantity": {
        "value": "string"
      },
      "remainingQuantity": {
        "value": "string"
      },
      "sltpOrder": {
        "accountId": "string",
        "symbol": "string",
        "side": "SIDE_UNSPECIFIED",
        "quantitySl": {
          "value": "string"
        },
        "slPrice": {
          "value": "string"
        },
        "limitPrice": {
          "value": "string"
        },
        "quantityTp": {
          "value": "string"
        },
        "tpPrice": {
          "value": "string"
        },
        "tpGuardSpread": {
          "value": "string"
        },
        "tpSpreadMeasure": "TP_SPREAD_MEASURE_UNDEFINED",
        "clientOrderId": "string",
        "validBefore": "VALID_BEFORE_UNSPECIFIED",
        "validExpiryTime": "2026-03-27T13:09:52.291Z",
        "comment": "string"
      }
    }
  ]
}
```

---

### Выставление биржевой заявки

`POST /v1/accounts/{accountId}/orders`

Поле `accountId` берется из URL-пути, остальные поля передаются в теле запроса.

**Path Parameters**

| Name | Type | Required | Description |
| --- | --- | --- | --- |
| accountId | string | yes | Идентификатор аккаунта |

**Body** (required, application/json)

| Name | Type | Required | Description |
| --- | --- | --- | --- |
| symbol | string | yes | Символ инструмента |
| quantity | object | yes | Количество (decimal value) |
| side | string (enum) | yes | Сторона сделки: `SIDE_UNSPECIFIED`, `SIDE_BUY` (покупка), `SIDE_SELL` (продажа) |
| type | string (enum) | yes | Тип заявки: `ORDER_TYPE_UNSPECIFIED`, `ORDER_TYPE_MARKET` (рыночная), `ORDER_TYPE_LIMIT` (лимитная), `ORDER_TYPE_STOP` (стоп рыночная), `ORDER_TYPE_STOP_LIMIT` (стоп лимитная), `ORDER_TYPE_MULTI_LEG` (мульти лег) |
| timeInForce | string (enum) | no | Срок действия: `TIME_IN_FORCE_UNSPECIFIED`, `TIME_IN_FORCE_DAY` (до конца дня), `TIME_IN_FORCE_GOOD_TILL_CANCEL` (до отмены), `TIME_IN_FORCE_GOOD_TILL_CROSSING` (до пересечения), `TIME_IN_FORCE_EXT` (внебиржевая), `TIME_IN_FORCE_ON_OPEN` (на открытии), `TIME_IN_FORCE_ON_CLOSE` (на закрытии), `TIME_IN_FORCE_IOC` (исполнить или отменить), `TIME_IN_FORCE_FOK` (исполнить полностью или отменить) |
| limitPrice | object | no | Лимитная цена (decimal value) |
| stopPrice | object | no | Стоп цена (decimal value) |
| stopCondition | string (enum) | no | Условие срабатывания стопа: `STOP_CONDITION_UNSPECIFIED`, `STOP_CONDITION_LAST_UP` (цена выше текущей), `STOP_CONDITION_LAST_DOWN` (цена ниже текущей) |
| legs | array | no | Ноги для мульти лег заявки |
| clientOrderId | string | no | Уникальный идентификатор заявки (максимум 20 символов). Автоматически генерируется, если не отправлен. |
| validBefore | string (enum) | no | Срок действия условной заявки: `VALID_BEFORE_UNSPECIFIED`, `VALID_BEFORE_END_OF_DAY` (до конца торгового дня), `VALID_BEFORE_GOOD_TILL_CANCEL` (до отмены), `VALID_BEFORE_GOOD_TILL_DATE` (до указанной даты-времени, только для SL/TP) |
| comment | string | no | Метка заявки (максимум 128 символов) |

**Responses**

| Code | Description |
| --- | --- |
| 200 | Успешный ответ |
| 400 | Неверно переданы торговые параметры |
| 401 | Срок действия токена истек или токен недействителен |
| 404 | Счёт или инструмент не были найдены |
| 429 | Слишком много запросов. Доступный лимит - 200 запросов в минуту |
| 500 | Внутренняя ошибка сервиса. Попробуйте позже |
| 503 | Сервис на данный момент не доступен. Попробуйте позже |
| 504 | Крайний срок истек до завершения операции |
| default | Непредвиденная ошибка |

**Request Example**

```curl
curl 'https://api.finam.ru/v1/accounts/{accountId}/orders' \
  --request POST \
  --header 'Content-Type: application/json' \
  --header '[REDACTED_SECRET]' \
  --data '{
  "symbol": "",
  "quantity": {
    "value": ""
  },
  "side": "SIDE_UNSPECIFIED",
  "type": "ORDER_TYPE_UNSPECIFIED",
  "timeInForce": "TIME_IN_FORCE_UNSPECIFIED",
  "limitPrice": {
    "value": ""
  },
  "stopPrice": {
    "value": ""
  },
  "stopCondition": "STOP_CONDITION_UNSPECIFIED",
  "legs": [
    {
      "symbol": "",
      "quantity": {
        "value": ""
      },
      "side": "SIDE_UNSPECIFIED"
    }
  ],
  "clientOrderId": "",
  "validBefore": "VALID_BEFORE_UNSPECIFIED",
  "comment": ""
}'
```

**Response Schema (200)**

```json
{
  "orderId": "string",
  "execId": "string",
  "status": "ORDER_STATUS_UNSPECIFIED",
  "order": {
    "accountId": "string",
    "symbol": "string",
    "quantity": {
      "value": "string"
    },
    "side": "SIDE_UNSPECIFIED",
    "type": "ORDER_TYPE_UNSPECIFIED",
    "timeInForce": "TIME_IN_FORCE_UNSPECIFIED",
    "limitPrice": {
      "value": "string"
    },
    "stopPrice": {
      "value": "string"
    },
    "stopCondition": "STOP_CONDITION_UNSPECIFIED",
    "legs": [
      {
        "symbol": "string",
        "quantity": {
          "value": "string"
        },
        "side": "SIDE_UNSPECIFIED"
      }
    ],
    "clientOrderId": "string",
    "validBefore": "VALID_BEFORE_UNSPECIFIED",
    "comment": "string"
  },
  "transactAt": "2026-03-27T13:09:52.291Z",
  "acceptAt": "2026-03-27T13:09:52.291Z",
  "withdrawAt": "2026-03-27T13:09:52.291Z",
  "initialQuantity": {
    "value": "string"
  },
  "executedQuantity": {
    "value": "string"
  },
  "remainingQuantity": {
    "value": "string"
  },
  "sltpOrder": {
    "accountId": "string",
    "symbol": "string",
    "side": "SIDE_UNSPECIFIED",
    "quantitySl": {
      "value": "string"
    },
    "slPrice": {
      "value": "string"
    },
    "limitPrice": {
      "value": "string"
    },
    "quantityTp": {
      "value": "string"
    },
    "tpPrice": {
      "value": "string"
    },
    "tpGuardSpread": {
      "value": "string"
    },
    "tpSpreadMeasure": "TP_SPREAD_MEASURE_UNDEFINED",
    "clientOrderId": "string",
    "validBefore": "VALID_BEFORE_UNSPECIFIED",
    "validExpiryTime": "2026-03-27T13:09:52.291Z",
    "comment": "string"
  }
}
```

---

### Получение информации о конкретном ордере

`GET /v1/accounts/{accountId}/orders/{orderId}`

**Path Parameters**

| Name | Type | Required | Description |
| --- | --- | --- | --- |
| accountId | string | yes | Идентификатор аккаунта |
| orderId | string | yes | Идентификатор заявки |

**Responses**

| Code | Description |
| --- | --- |
| 200 | Успешный ответ |
| 401 | Срок действия токена истек или токен недействителен |
| 404 | Счёт или заявка не были найдены |
| 429 | Слишком много запросов. Доступный лимит - 200 запросов в минуту |
| 500 | Внутренняя ошибка сервиса. Попробуйте позже |
| 503 | Сервис на данный момент не доступен. Попробуйте позже |
| 504 | Крайний срок истек до завершения операции |
| default | Непредвиденная ошибка |

**Request Example**

```curl
curl 'https://api.finam.ru/v1/accounts/{accountId}/orders/{orderId}' \
  --header '[REDACTED_SECRET]'
```

**Response Schema (200)**

```json
{
  "orderId": "string",
  "execId": "string",
  "status": "ORDER_STATUS_UNSPECIFIED",
  "order": {
    "accountId": "string",
    "symbol": "string",
    "quantity": {
      "value": "string"
    },
    "side": "SIDE_UNSPECIFIED",
    "type": "ORDER_TYPE_UNSPECIFIED",
    "timeInForce": "TIME_IN_FORCE_UNSPECIFIED",
    "limitPrice": {
      "value": "string"
    },
    "stopPrice": {
      "value": "string"
    },
    "stopCondition": "STOP_CONDITION_UNSPECIFIED",
    "legs": [
      {
        "symbol": "string",
        "quantity": {
          "value": "string"
        },
        "side": "SIDE_UNSPECIFIED"
      }
    ],
    "clientOrderId": "string",
    "validBefore": "VALID_BEFORE_UNSPECIFIED",
    "comment": "string"
  },
  "transactAt": "2026-03-27T13:09:52.291Z",
  "acceptAt": "2026-03-27T13:09:52.291Z",
  "withdrawAt": "2026-03-27T13:09:52.291Z",
  "initialQuantity": {
    "value": "string"
  },
  "executedQuantity": {
    "value": "string"
  },
  "remainingQuantity": {
    "value": "string"
  },
  "sltpOrder": {
    "accountId": "string",
    "symbol": "string",
    "side": "SIDE_UNSPECIFIED",
    "quantitySl": {
      "value": "string"
    },
    "slPrice": {
      "value": "string"
    },
    "limitPrice": {
      "value": "string"
    },
    "quantityTp": {
      "value": "string"
    },
    "tpPrice": {
      "value": "string"
    },
    "tpGuardSpread": {
      "value": "string"
    },
    "tpSpreadMeasure": "TP_SPREAD_MEASURE_UNDEFINED",
    "clientOrderId": "string",
    "validBefore": "VALID_BEFORE_UNSPECIFIED",
    "validExpiryTime": "2026-03-27T13:09:52.291Z",
    "comment": "string"
  }
}
```

---

### Отмена биржевой заявки

`DELETE /v1/accounts/{accountId}/orders/{orderId}`

**Path Parameters**

| Name | Type | Required | Description |
| --- | --- | --- | --- |
| accountId | string | yes | Идентификатор аккаунта |
| orderId | string | yes | Идентификатор заявки |

**Responses**

| Code | Description |
| --- | --- |
| 200 | Успешный ответ |
| 400 | Заявка не может быть отменена так как она уже исполнена |
| 401 | Срок действия токена истек или токен недействителен |
| 404 | Счёт или заявка не были найдены |
| 429 | Слишком много запросов. Доступный лимит - 200 запросов в минуту |
| 500 | Внутренняя ошибка сервиса. Попробуйте позже |
| 503 | Сервис на данный момент не доступен. Попробуйте позже |
| 504 | Крайний срок истек до завершения операции |
| default | Непредвиденная ошибка |

**Request Example**

```curl
curl 'https://api.finam.ru/v1/accounts/{accountId}/orders/{orderId}' \
  --request DELETE \
  --header '[REDACTED_SECRET]'
```

**Response Schema (200)**

```json
{
  "orderId": "string",
  "execId": "string",
  "status": "ORDER_STATUS_UNSPECIFIED",
  "order": {
    "accountId": "string",
    "symbol": "string",
    "quantity": {
      "value": "string"
    },
    "side": "SIDE_UNSPECIFIED",
    "type": "ORDER_TYPE_UNSPECIFIED",
    "timeInForce": "TIME_IN_FORCE_UNSPECIFIED",
    "limitPrice": {
      "value": "string"
    },
    "stopPrice": {
      "value": "string"
    },
    "stopCondition": "STOP_CONDITION_UNSPECIFIED",
    "legs": [
      {
        "symbol": "string",
        "quantity": {
          "value": "string"
        },
        "side": "SIDE_UNSPECIFIED"
      }
    ],
    "clientOrderId": "string",
    "validBefore": "VALID_BEFORE_UNSPECIFIED",
    "comment": "string"
  },
  "transactAt": "2026-03-27T13:09:52.291Z",
  "acceptAt": "2026-03-27T13:09:52.291Z",
  "withdrawAt": "2026-03-27T13:09:52.291Z",
  "initialQuantity": {
    "value": "string"
  },
  "executedQuantity": {
    "value": "string"
  },
  "remainingQuantity": {
    "value": "string"
  },
  "sltpOrder": {
    "accountId": "string",
    "symbol": "string",
    "side": "SIDE_UNSPECIFIED",
    "quantitySl": {
      "value": "string"
    },
    "slPrice": {
      "value": "string"
    },
    "limitPrice": {
      "value": "string"
    },
    "quantityTp": {
      "value": "string"
    },
    "tpPrice": {
      "value": "string"
    },
    "tpGuardSpread": {
      "value": "string"
    },
    "tpSpreadMeasure": "TP_SPREAD_MEASURE_UNDEFINED",
    "clientOrderId": "string",
    "validBefore": "VALID_BEFORE_UNSPECIFIED",
    "validExpiryTime": "2026-03-27T13:09:52.291Z",
    "comment": "string"
  }
}
```

---

### Выставление SL/TP заявки

`POST /v1/accounts/{accountId}/sltp-orders`

Поле `accountId` берется из URL-пути, остальные поля передаются в теле запроса.

**Path Parameters**

| Name | Type | Required | Description |
| --- | --- | --- | --- |
| accountId | string | yes | Идентификатор аккаунта |

**Body** (required, application/json)

| Name | Type | Required | Description |
| --- | --- | --- | --- |
| symbol | string | yes | Символ инструмента |
| side | string (enum) | yes | Сторона сделки: `SIDE_UNSPECIFIED`, `SIDE_BUY` (покупка), `SIDE_SELL` (продажа) |
| quantitySl | object | no | Количество для стоп-лосс (decimal value) |
| slPrice | object | no | Цена стоп-лосс (decimal value) |
| limitPrice | object | no | Лимитная цена (decimal value) |
| quantityTp | object | no | Количество для тейк-профит (decimal value) |
| tpPrice | object | no | Цена тейк-профит (decimal value) |
| tpGuardSpread | object | no | Величина защитного спреда для цены исполнения TP (decimal value) |
| tpSpreadMeasure | string (enum) | no | Единица измерения защитного спреда TP: `TP_SPREAD_MEASURE_UNDEFINED`, `TP_SPREAD_MEASURE_VALUE` (в единицах цены), `TP_SPREAD_MEASURE_PERCENT` (в процентах, до сотых) |
| clientOrderId | string | no | Уникальный идентификатор заявки (максимум 20 символов). Автоматически генерируется, если не отправлен. |
| validBefore | string (enum) | no | Срок действия: `VALID_BEFORE_UNSPECIFIED`, `VALID_BEFORE_END_OF_DAY` (до конца торгового дня), `VALID_BEFORE_GOOD_TILL_CANCEL` (до отмены), `VALID_BEFORE_GOOD_TILL_DATE` (до указанной даты-времени, только для SL/TP) |
| comment | string | no | Метка заявки (максимум 128 символов) |

**Responses**

| Code | Description |
| --- | --- |
| 200 | Успешный ответ |
| 400 | Неверно переданы торговые параметры |
| 401 | Срок действия токена истек или токен недействителен |
| 404 | Счёт или инструмент не были найдены |
| 429 | Слишком много запросов. Доступный лимит - 200 запросов в минуту |
| 500 | Внутренняя ошибка сервиса. Попробуйте позже |
| 503 | Сервис на данный момент не доступен. Попробуйте позже |
| 504 | Крайний срок истек до завершения операции |
| default | Непредвиденная ошибка |

**Request Example**

```curl
curl 'https://api.finam.ru/v1/accounts/{accountId}/sltp-orders' \
  --request POST \
  --header 'Content-Type: application/json' \
  --header '[REDACTED_SECRET]' \
  --data '{
  "symbol": "",
  "side": "SIDE_UNSPECIFIED",
  "quantitySl": {
    "value": ""
  },
  "slPrice": {
    "value": ""
  },
  "limitPrice": {
    "value": ""
  },
  "quantityTp": {
    "value": ""
  },
  "tpPrice": {
    "value": ""
  },
  "tpGuardSpread": {
    "value": ""
  },
  "tpSpreadMeasure": "TP_SPREAD_MEASURE_UNDEFINED",
  "clientOrderId": "",
  "validBefore": "VALID_BEFORE_UNSPECIFIED",
  "validExpiryTime": "",
  "comment": ""
}'
```

**Response Schema (200)**

```json
{
  "orderId": "string",
  "execId": "string",
  "status": "ORDER_STATUS_UNSPECIFIED",
  "order": {
    "accountId": "string",
    "symbol": "string",
    "quantity": {
      "value": "string"
    },
    "side": "SIDE_UNSPECIFIED",
    "type": "ORDER_TYPE_UNSPECIFIED",
    "timeInForce": "TIME_IN_FORCE_UNSPECIFIED",
    "limitPrice": {
      "value": "string"
    },
    "stopPrice": {
      "value": "string"
    },
    "stopCondition": "STOP_CONDITION_UNSPECIFIED",
    "legs": [
      {
        "symbol": "string",
        "quantity": {
          "value": "string"
        },
        "side": "SIDE_UNSPECIFIED"
      }
    ],
    "clientOrderId": "string",
    "validBefore": "VALID_BEFORE_UNSPECIFIED",
    "comment": "string"
  },
  "transactAt": "2026-03-27T13:09:52.291Z",
  "acceptAt": "2026-03-27T13:09:52.291Z",
  "withdrawAt": "2026-03-27T13:09:52.291Z",
  "initialQuantity": {
    "value": "string"
  },
  "executedQuantity": {
    "value": "string"
  },
  "remainingQuantity": {
    "value": "string"
  },
  "sltpOrder": {
    "accountId": "string",
    "symbol": "string",
    "side": "SIDE_UNSPECIFIED",
    "quantitySl": {
      "value": "string"
    },
    "slPrice": {
      "value": "string"
    },
    "limitPrice": {
      "value": "string"
    },
    "quantityTp": {
      "value": "string"
    },
    "tpPrice": {
      "value": "string"
    },
    "tpGuardSpread": {
      "value": "string"
    },
    "tpSpreadMeasure": "TP_SPREAD_MEASURE_UNDEFINED",
    "clientOrderId": "string",
    "validBefore": "VALID_BEFORE_UNSPECIFIED",
    "validExpiryTime": "2026-03-27T13:09:52.291Z",
    "comment": "string"
  }
}
```

---

## ReportsService

- [POST /v1/report](#запустить-генерацию-отчета-по-счету-за-период)
- [GET /v1/report/{reportId}/info](#получение-информации-о-результате-генерации-отчета-по-счету)

---

### Запустить генерацию отчета по счету за период

`POST /v1/report`

**Body** (required, application/json)

| Name | Type | Required | Description |
| --- | --- | --- | --- |
| accountId | string (int64) | yes | Идентификатор счета |
| dateRange | object | yes | Временной интервал |
| reportForm | string (enum) | no | Форма отчета: `REPORT_FORM_UNKNOWN` (не указана), `REPORT_FORM_SHORT` (краткая), `REPORT_FORM_LONG` (полная) |

**Responses**

| Code | Description |
| --- | --- |
| 200 | Успешный ответ |
| 401 | Срок действия токена истек или токен недействителен |
| 404 | Счёт не был найден в токене |
| 429 | Слишком много запросов. Доступный лимит - 200 запросов в минуту |
| 500 | Внутренняя ошибка сервиса. Попробуйте позже |
| 503 | Сервис на данный момент не доступен. Попробуйте позже |
| 504 | Крайний срок истек до завершения операции |
| default | Непредвиденная ошибка |

**Request Example**

```curl
curl https://api.finam.ru/v1/report \
  --request POST \
  --header 'Content-Type: application/json' \
  --header '[REDACTED_SECRET]' \
  --data '{
  "dateRange": {
    "dateBegin": "",
    "dateEnd": ""
  },
  "reportForm": "REPORT_FORM_UNKNOWN",
  "accountId": ""
}'
```

**Response Schema (200)**

```json
{
  "reportId": "string"
}
```

---

### Получение информации о результате генерации отчета по счету

`GET /v1/report/{reportId}/info`

**Path Parameters**

| Name | Type | Required | Description |
| --- | --- | --- | --- |
| reportId | string | yes | Идентификатор отчёта |

**Responses**

| Code | Description |
| --- | --- |
| 200 | Успешный ответ |
| 401 | Срок действия токена истек или токен недействителен |
| 404 | Счёт не был найден в токене |
| 429 | Слишком много запросов. Доступный лимит - 200 запросов в минуту |
| 500 | Внутренняя ошибка сервиса. Попробуйте позже |
| 503 | Сервис на данный момент не доступен. Попробуйте позже |
| 504 | Крайний срок истек до завершения операции |
| default | Непредвиденная ошибка |

**Request Example**

```curl
curl 'https://api.finam.ru/v1/report/{reportId}/info' \
  --header '[REDACTED_SECRET]'
```

**Response Schema (200)**

```json
{
  "info": {
    "reportId": "string",
    "status": "NOT_FOUND",
    "dateRange": {
      "dateBegin": "2026-03-27T13:09:52.291Z",
      "dateEnd": "2026-03-27T13:09:52.291Z"
    },
    "reportForm": "REPORT_FORM_UNKNOWN",
    "accountId": "string",
    "url": "string"
  }
}
```


Security scan verdicts — ClawScan: clean | static analysis: clean | VirusTotal: clean | SkillSpector: suspicious (CRITICAL)