# -*- coding: utf-8 -*-

# PLEASE DO NOT EDIT THIS FILE, IT IS GENERATED AND WILL BE OVERWRITTEN:
# https://github.com/ccxt_zagrava/ccxt_zagrava/blob/master/CONTRIBUTING.md#how-to-contribute-code

from ccxt_zagrava.base.exchange import Exchange
from ccxt_zagrava.abstract.alpaca import ImplicitAPI
from ccxt_zagrava.base.types import Int, Market, Num, Order, OrderBook, OrderSide, OrderType, Str, Trade
from typing import List
from ccxt_zagrava.base.errors import ExchangeError
from ccxt_zagrava.base.errors import PermissionDenied
from ccxt_zagrava.base.errors import BadRequest
from ccxt_zagrava.base.errors import BadSymbol
from ccxt_zagrava.base.errors import InsufficientFunds
from ccxt_zagrava.base.errors import InvalidOrder
from ccxt_zagrava.base.errors import NotSupported
from ccxt_zagrava.base.errors import RateLimitExceeded
from ccxt_zagrava.base.decimal_to_precision import TICK_SIZE


class alpaca(Exchange, ImplicitAPI):

    def describe(self):
        return self.deep_extend(super(alpaca, self).describe(), {
            'id': 'alpaca',
            'name': 'Alpaca',
            'countries': ['US'],
            # 3 req/s for free
            # 150 req/s for subscribers: https://alpaca.markets/data
            # for brokers: https://alpaca.markets/docs/api-references/broker-api/#authentication-and-rate-limit
            'rateLimit': 333,
            'hostname': 'alpaca.markets',
            'pro': True,
            'urls': {
                'logo': 'https://user-images.githubusercontent.com/1294454/187234005-b864db3d-f1e3-447a-aaf9-a9fc7b955d07.jpg',
                'www': 'https://alpaca.markets',
                'api': {
                    'broker': 'https://broker-api.{hostname}',
                    'trader': 'https://api.{hostname}',
                    'market': 'https://data.{hostname}',
                },
                'test': {
                    'broker': 'https://broker-api.sandbox.{hostname}',
                    'trader': 'https://paper-api.{hostname}',
                    'market': 'https://data.sandbox.{hostname}',
                },
                'doc': 'https://alpaca.markets/docs/',
                'fees': 'https://docs.alpaca.markets/docs/crypto-fees',
            },
            'has': {
                'CORS': False,
                'spot': True,
                'margin': False,
                'swap': False,
                'future': False,
                'option': False,
                'cancelAllOrders': True,
                'cancelOrder': True,
                'closeAllPositions': False,
                'closePosition': False,
                'createOrder': True,
                'fetchBalance': False,
                'fetchBidsAsks': False,
                'fetchClosedOrders': True,
                'fetchCurrencies': False,
                'fetchDepositAddress': False,
                'fetchDepositAddressesByNetwork': False,
                'fetchDeposits': False,
                'fetchDepositsWithdrawals': False,
                'fetchFundingHistory': False,
                'fetchFundingRate': False,
                'fetchFundingRates': False,
                'fetchL1OrderBook': True,
                'fetchL2OrderBook': False,
                'fetchMarkets': True,
                'fetchMyTrades': False,
                'fetchOHLCV': True,
                'fetchOpenOrder': False,
                'fetchOpenOrders': True,
                'fetchOrder': True,
                'fetchOrderBook': True,
                'fetchOrders': True,
                'fetchPosition': False,
                'fetchPositionHistory': False,
                'fetchPositionMode': False,
                'fetchPositions': False,
                'fetchPositionsForSymbol': False,
                'fetchPositionsHistory': False,
                'fetchPositionsRisk': False,
                'fetchStatus': False,
                'fetchTicker': False,
                'fetchTickers': False,
                'fetchTime': True,
                'fetchTrades': True,
                'fetchTradingFee': False,
                'fetchTradingFees': False,
                'fetchTransactionFees': False,
                'fetchTransactions': False,
                'fetchTransfers': False,
                'fetchWithdrawals': False,
                'sandbox': True,
                'setLeverage': False,
                'setMarginMode': False,
                'transfer': False,
                'withdraw': False,
            },
            'api': {
                'broker': {
                },
                'trader': {
                    'private': {
                        'get': [
                            'v2/account',
                            'v2/orders',
                            'v2/orders/{order_id}',
                            'v2/positions',
                            'v2/positions/{symbol_or_asset_id}',
                            'v2/account/portfolio/history',
                            'v2/watchlists',
                            'v2/watchlists/{watchlist_id}',
                            'v2/watchlists:by_name',
                            'v2/account/configurations',
                            'v2/account/activities',
                            'v2/account/activities/{activity_type}',
                            'v2/calendar',
                            'v2/clock',
                            'v2/assets',
                            'v2/assets/{symbol_or_asset_id}',
                            'v2/corporate_actions/announcements/{id}',
                            'v2/corporate_actions/announcements',
                        ],
                        'post': [
                            'v2/orders',
                            'v2/watchlists',
                            'v2/watchlists/{watchlist_id}',
                            'v2/watchlists:by_name',
                        ],
                        'put': [
                            'v2/watchlists/{watchlist_id}',
                            'v2/watchlists:by_name',
                        ],
                        'patch': [
                            'v2/orders/{order_id}',
                            'v2/account/configurations',
                        ],
                        'delete': [
                            'v2/orders',
                            'v2/orders/{order_id}',
                            'v2/positions',
                            'v2/positions/{symbol_or_asset_id}',
                            'v2/watchlists/{watchlist_id}',
                            'v2/watchlists:by_name',
                            'v2/watchlists/{watchlist_id}/{symbol}',
                        ],
                    },
                },
                'market': {
                    'public': {
                        'get': [
                            'v1beta3/crypto/{loc}/bars',
                            'v1beta3/crypto/{loc}/latest/bars',
                            'v1beta3/crypto/{loc}/latest/orderbooks',
                            'v1beta3/crypto/{loc}/latest/quotes',
                            'v1beta3/crypto/{loc}/latest/trades',
                            'v1beta3/crypto/{loc}/quotes',
                            'v1beta3/crypto/{loc}/snapshots',
                            'v1beta3/crypto/{loc}/trades',
                        ],
                    },
                    'private': {
                        'get': [
                            'v1beta1/corporate-actions',
                            'v1beta1/forex/latest/rates',
                            'v1beta1/forex/rates',
                            'v1beta1/logos/{symbol}',
                            'v1beta1/news',
                            'v1beta1/screener/stocks/most-actives',
                            'v1beta1/screener/{market_type}/movers',
                            'v2/stocks/auctions',
                            'v2/stocks/bars',
                            'v2/stocks/bars/latest',
                            'v2/stocks/meta/conditions/{ticktype}',
                            'v2/stocks/meta/exchanges',
                            'v2/stocks/quotes',
                            'v2/stocks/quotes/latest',
                            'v2/stocks/snapshots',
                            'v2/stocks/trades',
                            'v2/stocks/trades/latest',
                            'v2/stocks/{symbol}/auctions',
                            'v2/stocks/{symbol}/bars',
                            'v2/stocks/{symbol}/bars/latest',
                            'v2/stocks/{symbol}/quotes',
                            'v2/stocks/{symbol}/quotes/latest',
                            'v2/stocks/{symbol}/snapshot',
                            'v2/stocks/{symbol}/trades',
                            'v2/stocks/{symbol}/trades/latest',
                        ],
                    },
                },
            },
            'timeframes': {
                '1m': '1min',
                '3m': '3min',
                '5m': '5min',
                '15m': '15min',
                '30m': '30min',
                '1h': '1H',
                '2h': '2H',
                '4h': '4H',
                '6h': '6H',
                '8h': '8H',
                '12h': '12H',
                '1d': '1D',
                '3d': '3D',
                '1w': '1W',
                '1M': '1M',
            },
            'precisionMode': TICK_SIZE,
            'requiredCredentials': {
                'apiKey': True,
                'secret': True,
            },
            'fees': {
                'trading': {
                    'tierBased': True,
                    'percentage': True,
                    'maker': self.parse_number('0.0015'),
                    'taker': self.parse_number('0.0025'),
                    'tiers': {
                        'taker': [
                            [self.parse_number('0'), self.parse_number('0.0025')],
                            [self.parse_number('100000'), self.parse_number('0.0022')],
                            [self.parse_number('500000'), self.parse_number('0.0020')],
                            [self.parse_number('1000000'), self.parse_number('0.0018')],
                            [self.parse_number('10000000'), self.parse_number('0.0015')],
                            [self.parse_number('25000000'), self.parse_number('0.0013')],
                            [self.parse_number('50000000'), self.parse_number('0.0012')],
                            [self.parse_number('100000000'), self.parse_number('0.001')],
                        ],
                        'maker': [
                            [self.parse_number('0'), self.parse_number('0.0015')],
                            [self.parse_number('100000'), self.parse_number('0.0012')],
                            [self.parse_number('500000'), self.parse_number('0.001')],
                            [self.parse_number('1000000'), self.parse_number('0.0008')],
                            [self.parse_number('10000000'), self.parse_number('0.0005')],
                            [self.parse_number('25000000'), self.parse_number('0.0002')],
                            [self.parse_number('50000000'), self.parse_number('0.0002')],
                            [self.parse_number('100000000'), self.parse_number('0.00')],
                        ],
                    },
                },
            },
            'headers': {
                'APCA-PARTNER-ID': 'ccxt_zagrava',
            },
            'options': {
                'defaultExchange': 'CBSE',
                'exchanges': [
                    'CBSE',  # Coinbase
                    'FTX',  # FTXUS
                    'GNSS',  # Genesis
                    'ERSX',  # ErisX
                ],
                'defaultTimeInForce': 'gtc',  # fok, gtc, ioc
                'clientOrderId': 'ccxt_zagrava_{id}',
            },
            'exceptions': {
                'exact': {
                    'forbidden.': PermissionDenied,  # {"message": "forbidden."}
                    '40410000': InvalidOrder,  # {"code": 40410000, "message": "order is not found."}
                    '40010001': BadRequest,  # {"code":40010001,"message":"invalid order type for crypto order"}
                    '40110000': PermissionDenied,  # {"code": 40110000, "message": "request is not authorized"}
                    '40310000': InsufficientFunds,  # {"available":"0","balance":"0","code":40310000,"message":"insufficient balance for USDT(requested: 221.63, available: 0)","symbol":"USDT"}
                    '42910000': RateLimitExceeded,  # {"code":42910000,"message":"rate limit exceeded"}
                },
                'broad': {
                    'Invalid format for parameter': BadRequest,  # {"message":"Invalid format for parameter start: error parsing '0' or 2006-01-02 time: parsing time \"0\" as \"2006-01-02\": cannot parse \"0\" as \"2006\""}
                    'Invalid symbol': BadSymbol,  # {"message":"Invalid symbol(s): BTC/USDdsda does not match ^[A-Z]+/[A-Z]+$"}
                },
            },
        })

    def fetch_time(self, params={}):
        """
        fetches the current integer timestamp in milliseconds from the exchange server
        :param dict [params]: extra parameters specific to the exchange API endpoint
        :returns int: the current integer timestamp in milliseconds from the exchange server
        """
        response = self.traderPrivateGetV2Clock(params)
        #
        #     {
        #         timestamp: '2023-11-22T08:07:57.654738097-05:00',
        #         is_open: False,
        #         next_open: '2023-11-22T09:30:00-05:00',
        #         next_close: '2023-11-22T16:00:00-05:00'
        #     }
        #
        timestamp = self.safe_string(response, 'timestamp')
        localTime = timestamp[0:23]
        jetlagStrStart = len(timestamp) - 6
        jetlagStrEnd = len(timestamp) - 3
        jetlag = timestamp[jetlagStrStart:jetlagStrEnd]
        iso = self.parse8601(localTime) - self.parse_to_numeric(jetlag) * 3600 * 1000
        return iso

    def fetch_markets(self, params={}) -> List[Market]:
        """
        retrieves data on all markets for alpaca
        :see: https://docs.alpaca.markets/reference/get-v2-assets
        :param dict [params]: extra parameters specific to the exchange api endpoint
        :returns dict[]: an array of objects representing market data
        """
        request: dict = {
            'asset_class': 'crypto',
            'status': 'active',
        }
        assets = self.traderPrivateGetV2Assets(self.extend(request, params))
        #
        #     [
        #         {
        #             "id": "c150e086-1e75-44e6-9c2c-093bb1e93139",
        #             "class": "crypto",
        #             "exchange": "CRYPTO",
        #             "symbol": "BTC/USDT",
        #             "name": "Bitcoin / USD Tether",
        #             "status": "active",
        #             "tradable": True,
        #             "marginable": False,
        #             "maintenance_margin_requirement": 100,
        #             "shortable": False,
        #             "easy_to_borrow": False,
        #             "fractionable": True,
        #             "attributes": [],
        #             "min_order_size": "0.000026873",
        #             "min_trade_increment": "0.000000001",
        #             "price_increment": "1"
        #         }
        #     ]
        #
        return self.parse_markets(assets)

    def parse_market(self, asset) -> Market:
        #
        #     {
        #         "id": "c150e086-1e75-44e6-9c2c-093bb1e93139",
        #         "class": "crypto",
        #         "exchange": "CRYPTO",
        #         "symbol": "BTC/USDT",
        #         "name": "Bitcoin / USD Tether",
        #         "status": "active",
        #         "tradable": True,
        #         "marginable": False,
        #         "maintenance_margin_requirement": 100,
        #         "shortable": False,
        #         "easy_to_borrow": False,
        #         "fractionable": True,
        #         "attributes": [],
        #         "min_order_size": "0.000026873",
        #         "min_trade_increment": "0.000000001",
        #         "price_increment": "1"
        #     }
        #
        marketId = self.safe_string(asset, 'symbol')
        parts = marketId.split('/')
        assetClass = self.safe_string(asset, 'class')
        baseId = self.safe_string(parts, 0)
        quoteId = self.safe_string(parts, 1)
        base = self.safe_currency_code(baseId)
        quote = self.safe_currency_code(quoteId)
        # Us equity markets do not include quote in symbol.
        # We can safely coerce us_equity quote to USD
        if quote is None and assetClass == 'us_equity':
            quote = 'USD'
        symbol = base + '/' + quote
        status = self.safe_string(asset, 'status')
        active = (status == 'active')
        minAmount = self.safe_number(asset, 'min_order_size')
        amount = self.safe_number(asset, 'min_trade_increment')
        price = self.safe_number(asset, 'price_increment')
        return {
            'id': marketId,
            'symbol': symbol,
            'base': base,
            'quote': quote,
            'settle': None,
            'baseId': baseId,
            'quoteId': quoteId,
            'settleId': None,
            'type': 'spot',
            'spot': True,
            'margin': None,
            'swap': False,
            'future': False,
            'option': False,
            'active': active,
            'contract': False,
            'linear': None,
            'inverse': None,
            'contractSize': None,
            'expiry': None,
            'expiryDatetime': None,
            'strike': None,
            'optionType': None,
            'precision': {
                'amount': amount,
                'price': price,
            },
            'limits': {
                'leverage': {
                    'min': None,
                    'max': None,
                },
                'amount': {
                    'min': minAmount,
                    'max': None,
                },
                'price': {
                    'min': None,
                    'max': None,
                },
                'cost': {
                    'min': None,
                    'max': None,
                },
            },
            'created': None,
            'info': asset,
        }

    def fetch_trades(self, symbol: str, since: Int = None, limit: Int = None, params={}) -> List[Trade]:
        """
        get the list of most recent trades for a particular symbol
        :see: https://docs.alpaca.markets/reference/cryptotrades
        :see: https://docs.alpaca.markets/reference/cryptolatesttrades
        :param str symbol: unified symbol of the market to fetch trades for
        :param int [since]: timestamp in ms of the earliest trade to fetch
        :param int [limit]: the maximum amount of trades to fetch
        :param dict [params]: extra parameters specific to the exchange API endpoint
        :param str [params.loc]: crypto location, default: us
        :param str [params.method]: method, default: marketPublicGetV1beta3CryptoLocTrades
        :returns Trade[]: a list of `trade structures <https://docs.ccxt_zagrava.com/#/?id=public-trades>`
        """
        self.load_markets()
        market = self.market(symbol)
        marketId = market['id']
        loc = self.safe_string(params, 'loc', 'us')
        method = self.safe_string(params, 'method', 'marketPublicGetV1beta3CryptoLocTrades')
        request: dict = {
            'symbols': marketId,
            'loc': loc,
        }
        params = self.omit(params, ['loc', 'method'])
        symbolTrades = None
        if method == 'marketPublicGetV1beta3CryptoLocTrades':
            if since is not None:
                request['start'] = self.iso8601(since)
            if limit is not None:
                request['limit'] = limit
            response = self.marketPublicGetV1beta3CryptoLocTrades(self.extend(request, params))
            #
            #    {
            #        "next_page_token": null,
            #        "trades": {
            #            "BTC/USD": [
            #                {
            #                    "i": 36440704,
            #                    "p": 22625,
            #                    "s": 0.0001,
            #                    "t": "2022-07-21T11:47:31.073391Z",
            #                    "tks": "B"
            #                }
            #            ]
            #        }
            #    }
            #
            trades = self.safe_dict(response, 'trades', {})
            symbolTrades = self.safe_list(trades, marketId, [])
        elif method == 'marketPublicGetV1beta3CryptoLocLatestTrades':
            response = self.marketPublicGetV1beta3CryptoLocLatestTrades(self.extend(request, params))
            #
            #    {
            #       "trades": {
            #            "BTC/USD": {
            #                "i": 36440704,
            #                "p": 22625,
            #                "s": 0.0001,
            #                "t": "2022-07-21T11:47:31.073391Z",
            #                "tks": "B"
            #            }
            #        }
            #    }
            #
            trades = self.safe_dict(response, 'trades', {})
            symbolTrades = self.safe_dict(trades, marketId, {})
            symbolTrades = [symbolTrades]
        else:
            raise NotSupported(self.id + ' fetchTrades() does not support ' + method + ', marketPublicGetV1beta3CryptoLocTrades and marketPublicGetV1beta3CryptoLocLatestTrades are supported')
        return self.parse_trades(symbolTrades, market, since, limit)

    def fetch_order_book(self, symbol: str, limit: Int = None, params={}) -> OrderBook:
        """
        fetches information on open orders with bid(buy) and ask(sell) prices, volumes and other data
        :see: https://docs.alpaca.markets/reference/cryptolatestorderbooks
        :param str symbol: unified symbol of the market to fetch the order book for
        :param int [limit]: the maximum amount of order book entries to return
        :param dict [params]: extra parameters specific to the exchange API endpoint
        :param str [params.loc]: crypto location, default: us
        :returns dict: A dictionary of `order book structures <https://github.com/ccxt_zagrava/ccxt_zagrava/wiki/Manual#order-book-structure>` indexed by market symbols
        """
        self.load_markets()
        market = self.market(symbol)
        id = market['id']
        loc = self.safe_string(params, 'loc', 'us')
        request: dict = {
            'symbols': id,
            'loc': loc,
        }
        response = self.marketPublicGetV1beta3CryptoLocLatestOrderbooks(self.extend(request, params))
        #
        #   {
        #       "orderbooks":{
        #          "BTC/USD":{
        #             "a":[
        #                {
        #                   "p":22208,
        #                   "s":0.0051
        #                },
        #                {
        #                   "p":22209,
        #                   "s":0.1123
        #                },
        #                {
        #                   "p":22210,
        #                   "s":0.2465
        #                }
        #             ],
        #             "b":[
        #                {
        #                   "p":22203,
        #                   "s":0.395
        #                },
        #                {
        #                   "p":22202,
        #                   "s":0.2465
        #                },
        #                {
        #                   "p":22201,
        #                   "s":0.6455
        #                }
        #             ],
        #             "t":"2022-07-19T13:41:55.13210112Z"
        #          }
        #       }
        #   }
        #
        orderbooks = self.safe_value(response, 'orderbooks', {})
        rawOrderbook = self.safe_value(orderbooks, id, {})
        timestamp = self.parse8601(self.safe_string(rawOrderbook, 't'))
        return self.parse_order_book(rawOrderbook, market['symbol'], timestamp, 'b', 'a', 'p', 's')

    def fetch_ohlcv(self, symbol: str, timeframe='1m', since: Int = None, limit: Int = None, params={}) -> List[list]:
        """
        fetches historical candlestick data containing the open, high, low, and close price, and the volume of a market
        :see: https://docs.alpaca.markets/reference/cryptobars
        :see: https://docs.alpaca.markets/reference/cryptolatestbars
        :param str symbol: unified symbol of the market to fetch OHLCV data for
        :param str timeframe: the length of time each candle represents
        :param int [since]: timestamp in ms of the earliest candle to fetch
        :param int [limit]: the maximum amount of candles to fetch
        :param dict [params]: extra parameters specific to the alpha api endpoint
        :param str [params.loc]: crypto location, default: us
        :param str [params.method]: method, default: marketPublicGetV1beta3CryptoLocBars
        :returns int[][]: A list of candles ordered, open, high, low, close, volume
        """
        self.load_markets()
        market = self.market(symbol)
        marketId = market['id']
        loc = self.safe_string(params, 'loc', 'us')
        method = self.safe_string(params, 'method', 'marketPublicGetV1beta3CryptoLocBars')
        request: dict = {
            'symbols': marketId,
            'loc': loc,
        }
        params = self.omit(params, ['loc', 'method'])
        ohlcvs = None
        if method == 'marketPublicGetV1beta3CryptoLocBars':
            if limit is not None:
                request['limit'] = limit
            if since is not None:
                request['start'] = self.yyyymmdd(since)
            request['timeframe'] = self.safe_string(self.timeframes, timeframe, timeframe)
            response = self.marketPublicGetV1beta3CryptoLocBars(self.extend(request, params))
            #
            #    {
            #        "bars": {
            #           "BTC/USD": [
            #              {
            #                 "c": 22887,
            #                 "h": 22888,
            #                 "l": 22873,
            #                 "n": 11,
            #                 "o": 22883,
            #                 "t": "2022-07-21T05:00:00Z",
            #                 "v": 1.1138,
            #                 "vw": 22883.0155324116
            #              },
            #              {
            #                 "c": 22895,
            #                 "h": 22895,
            #                 "l": 22884,
            #                 "n": 6,
            #                 "o": 22884,
            #                 "t": "2022-07-21T05:01:00Z",
            #                 "v": 0.001,
            #                 "vw": 22889.5
            #              }
            #           ]
            #        },
            #        "next_page_token": "QlRDL1VTRHxNfDIwMjItMDctMjFUMDU6MDE6MDAuMDAwMDAwMDAwWg=="
            #     }
            #
            bars = self.safe_dict(response, 'bars', {})
            ohlcvs = self.safe_list(bars, marketId, [])
        elif method == 'marketPublicGetV1beta3CryptoLocLatestBars':
            response = self.marketPublicGetV1beta3CryptoLocLatestBars(self.extend(request, params))
            #
            #    {
            #        "bars": {
            #           "BTC/USD": {
            #              "c": 22887,
            #              "h": 22888,
            #              "l": 22873,
            #              "n": 11,
            #              "o": 22883,
            #              "t": "2022-07-21T05:00:00Z",
            #              "v": 1.1138,
            #              "vw": 22883.0155324116
            #           }
            #        }
            #     }
            #
            bars = self.safe_dict(response, 'bars', {})
            ohlcvs = self.safe_dict(bars, marketId, {})
            ohlcvs = [ohlcvs]
        else:
            raise NotSupported(self.id + ' fetchOHLCV() does not support ' + method + ', marketPublicGetV1beta3CryptoLocBars and marketPublicGetV1beta3CryptoLocLatestBars are supported')
        return self.parse_ohlcvs(ohlcvs, market, timeframe, since, limit)

    def parse_ohlcv(self, ohlcv, market: Market = None) -> list:
        #
        #     {
        #        "c":22895,
        #        "h":22895,
        #        "l":22884,
        #        "n":6,
        #        "o":22884,
        #        "t":"2022-07-21T05:01:00Z",
        #        "v":0.001,
        #        "vw":22889.5
        #     }
        #
        datetime = self.safe_string(ohlcv, 't')
        timestamp = self.parse8601(datetime)
        return [
            timestamp,  # timestamp
            self.safe_number(ohlcv, 'o'),  # open
            self.safe_number(ohlcv, 'h'),  # high
            self.safe_number(ohlcv, 'l'),  # low
            self.safe_number(ohlcv, 'c'),  # close
            self.safe_number(ohlcv, 'v'),  # volume
        ]

    def create_order(self, symbol: str, type: OrderType, side: OrderSide, amount: float, price: Num = None, params={}):
        """
        create a trade order
        :see: https://docs.alpaca.markets/reference/postorder
        :param str symbol: unified symbol of the market to create an order in
        :param str type: 'market', 'limit' or 'stop_limit'
        :param str side: 'buy' or 'sell'
        :param float amount: how much of currency you want to trade in units of base currency
        :param float [price]: the price at which the order is to be fulfilled, in units of the quote currency, ignored in market orders
        :param dict [params]: extra parameters specific to the exchange API endpoint
        :param float [params.triggerPrice]: The price at which a trigger order is triggered at
        :returns dict: an `order structure <https://docs.ccxt_zagrava.com/#/?id=order-structure>`
        """
        self.load_markets()
        market = self.market(symbol)
        id = market['id']
        request: dict = {
            'symbol': id,
            'qty': self.amount_to_precision(symbol, amount),
            'side': side,
            'type': type,  # market, limit, stop_limit
        }
        triggerPrice = self.safe_string_n(params, ['triggerPrice', 'stop_price'])
        if triggerPrice is not None:
            newType = None
            if type.find('limit') >= 0:
                newType = 'stop_limit'
            else:
                raise NotSupported(self.id + ' createOrder() does not support stop orders for ' + type + ' orders, only stop_limit orders are supported')
            request['stop_price'] = self.price_to_precision(symbol, triggerPrice)
            request['type'] = newType
        if type.find('limit') >= 0:
            request['limit_price'] = self.price_to_precision(symbol, price)
        defaultTIF = self.safe_string(self.options, 'defaultTimeInForce')
        request['time_in_force'] = self.safe_string(params, 'timeInForce', defaultTIF)
        params = self.omit(params, ['timeInForce', 'triggerPrice'])
        clientOrderIdprefix = self.safe_string(self.options, 'clientOrderId')
        uuid = self.uuid()
        parts = uuid.split('-')
        random_id = ''.join(parts)
        defaultClientId = self.implode_params(clientOrderIdprefix, {'id': random_id})
        clientOrderId = self.safe_string(params, 'clientOrderId', defaultClientId)
        request['client_order_id'] = clientOrderId
        params = self.omit(params, ['clientOrderId'])
        order = self.traderPrivatePostV2Orders(self.extend(request, params))
        #
        #   {
        #      "id": "61e69015-8549-4bfd-b9c3-01e75843f47d",
        #      "client_order_id": "eb9e2aaa-f71a-4f51-b5b4-52a6c565dad4",
        #      "created_at": "2021-03-16T18:38:01.942282Z",
        #      "updated_at": "2021-03-16T18:38:01.942282Z",
        #      "submitted_at": "2021-03-16T18:38:01.937734Z",
        #      "filled_at": null,
        #      "expired_at": null,
        #      "canceled_at": null,
        #      "failed_at": null,
        #      "replaced_at": null,
        #      "replaced_by": null,
        #      "replaces": null,
        #      "asset_id": "b0b6dd9d-8b9b-48a9-ba46-b9d54906e415",
        #      "symbol": "AAPL",
        #      "asset_class": "us_equity",
        #      "notional": "500",
        #      "qty": null,
        #      "filled_qty": "0",
        #      "filled_avg_price": null,
        #      "order_class": "",
        #      "order_type": "market",
        #      "type": "market",
        #      "side": "buy",
        #      "time_in_force": "day",
        #      "limit_price": null,
        #      "stop_price": null,
        #      "status": "accepted",
        #      "extended_hours": False,
        #      "legs": null,
        #      "trail_percent": null,
        #      "trail_price": null,
        #      "hwm": null
        #   }
        #
        return self.parse_order(order, market)

    def cancel_order(self, id: str, symbol: Str = None, params={}):
        """
        cancels an open order
        :see: https://docs.alpaca.markets/reference/deleteorderbyorderid
        :param str id: order id
        :param str symbol: unified symbol of the market the order was made in
        :param dict [params]: extra parameters specific to the exchange API endpoint
        :returns dict: An `order structure <https://docs.ccxt_zagrava.com/#/?id=order-structure>`
        """
        request: dict = {
            'order_id': id,
        }
        response = self.traderPrivateDeleteV2OrdersOrderId(self.extend(request, params))
        #
        #   {
        #       "code": 40410000,
        #       "message": "order is not found."
        #   }
        #
        return self.parse_order(response)

    def cancel_all_orders(self, symbol: Str = None, params={}):
        """
        cancel all open orders in a market
        :see: https://docs.alpaca.markets/reference/deleteallorders
        :param str symbol: alpaca cancelAllOrders cannot setting symbol, it will cancel all open orders
        :param dict [params]: extra parameters specific to the exchange API endpoint
        :returns dict[]: a list of `order structures <https://docs.ccxt_zagrava.com/#/?id=order-structure>`
        """
        self.load_markets()
        response = self.traderPrivateDeleteV2Orders(params)
        if isinstance(response, list):
            return self.parse_orders(response, None)
        else:
            return [
                self.safe_order({
                    'info': response,
                }),
            ]

    def fetch_order(self, id: str, symbol: Str = None, params={}):
        """
        fetches information on an order made by the user
        :see: https://docs.alpaca.markets/reference/getorderbyorderid
        :param str symbol: unified symbol of the market the order was made in
        :param dict [params]: extra parameters specific to the exchange API endpoint
        :returns dict: An `order structure <https://docs.ccxt_zagrava.com/#/?id=order-structure>`
        """
        self.load_markets()
        request: dict = {
            'order_id': id,
        }
        order = self.traderPrivateGetV2OrdersOrderId(self.extend(request, params))
        marketId = self.safe_string(order, 'symbol')
        market = self.safe_market(marketId)
        return self.parse_order(order, market)

    def fetch_orders(self, symbol: Str = None, since: Int = None, limit: Int = None, params={}) -> List[Order]:
        """
        fetches information on multiple orders made by the user
        :see: https://docs.alpaca.markets/reference/getallorders
        :param str symbol: unified market symbol of the market orders were made in
        :param int [since]: the earliest time in ms to fetch orders for
        :param int [limit]: the maximum number of order structures to retrieve
        :param dict [params]: extra parameters specific to the exchange API endpoint
        :param int [params.until]: the latest time in ms to fetch orders for
        :returns Order[]: a list of `order structures <https://docs.ccxt_zagrava.com/#/?id=order-structure>`
        """
        self.load_markets()
        request: dict = {
            'status': 'all',
        }
        market = None
        if symbol is not None:
            market = self.market(symbol)
            request['symbols'] = market['id']
        until = self.safe_integer(params, 'until')
        if until is not None:
            params = self.omit(params, 'until')
            request['endTime'] = until
        if since is not None:
            request['after'] = since
        if limit is not None:
            request['limit'] = limit
        response = self.traderPrivateGetV2Orders(self.extend(request, params))
        #
        #     [
        #         {
        #           "id": "cbaf12d7-69b8-49c0-a31b-b46af35c755c",
        #           "client_order_id": "ccxt_zagrava_b36156ae6fd44d098ac9c179bab33efd",
        #           "created_at": "2023-11-17T04:21:42.234579Z",
        #           "updated_at": "2023-11-17T04:22:34.442765Z",
        #           "submitted_at": "2023-11-17T04:21:42.233357Z",
        #           "filled_at": null,
        #           "expired_at": null,
        #           "canceled_at": "2023-11-17T04:22:34.399019Z",
        #           "failed_at": null,
        #           "replaced_at": null,
        #           "replaced_by": null,
        #           "replaces": null,
        #           "asset_id": "77c6f47f-0939-4b23-b41e-47b4469c4bc8",
        #           "symbol": "LTC/USDT",
        #           "asset_class": "crypto",
        #           "notional": null,
        #           "qty": "0.001",
        #           "filled_qty": "0",
        #           "filled_avg_price": null,
        #           "order_class": "",
        #           "order_type": "limit",
        #           "type": "limit",
        #           "side": "sell",
        #           "time_in_force": "gtc",
        #           "limit_price": "1000",
        #           "stop_price": null,
        #           "status": "canceled",
        #           "extended_hours": False,
        #           "legs": null,
        #           "trail_percent": null,
        #           "trail_price": null,
        #           "hwm": null,
        #           "subtag": null,
        #           "source": "access_key"
        #         }
        #     ]
        #
        return self.parse_orders(response, market, since, limit)

    def fetch_open_orders(self, symbol: Str = None, since: Int = None, limit: Int = None, params={}) -> List[Order]:
        """
        fetch all unfilled currently open orders
        :see: https://docs.alpaca.markets/reference/getallorders
        :param str symbol: unified market symbol of the market orders were made in
        :param int [since]: the earliest time in ms to fetch orders for
        :param int [limit]: the maximum number of order structures to retrieve
        :param dict [params]: extra parameters specific to the exchange API endpoint
        :param int [params.until]: the latest time in ms to fetch orders for
        :returns Order[]: a list of `order structures <https://docs.ccxt_zagrava.com/#/?id=order-structure>`
        """
        request: dict = {
            'status': 'open',
        }
        return self.fetch_orders(symbol, since, limit, self.extend(request, params))

    def fetch_closed_orders(self, symbol: Str = None, since: Int = None, limit: Int = None, params={}) -> List[Order]:
        """
        fetches information on multiple closed orders made by the user
        :see: https://docs.alpaca.markets/reference/getallorders
        :param str symbol: unified market symbol of the market orders were made in
        :param int [since]: the earliest time in ms to fetch orders for
        :param int [limit]: the maximum number of order structures to retrieve
        :param dict [params]: extra parameters specific to the exchange API endpoint
        :param int [params.until]: the latest time in ms to fetch orders for
        :returns Order[]: a list of `order structures <https://docs.ccxt_zagrava.com/#/?id=order-structure>`
        """
        request: dict = {
            'status': 'closed',
        }
        return self.fetch_orders(symbol, since, limit, self.extend(request, params))

    def parse_order(self, order: dict, market: Market = None) -> Order:
        #
        #    {
        #        "id":"6ecfcc34-4bed-4b53-83ba-c564aa832a81",
        #        "client_order_id":"ccxt_zagrava_1c6ceab0b5e84727b2f1c0394ba17560",
        #        "created_at":"2022-06-14T13:59:30.224037068Z",
        #        "updated_at":"2022-06-14T13:59:30.224037068Z",
        #        "submitted_at":"2022-06-14T13:59:30.221856828Z",
        #        "filled_at":null,
        #        "expired_at":null,
        #        "canceled_at":null,
        #        "failed_at":null,
        #        "replaced_at":null,
        #        "replaced_by":null,
        #        "replaces":null,
        #        "asset_id":"64bbff51-59d6-4b3c-9351-13ad85e3c752",
        #        "symbol":"BTCUSD",
        #        "asset_class":"crypto",
        #        "notional":null,
        #        "qty":"0.01",
        #        "filled_qty":"0",
        #        "filled_avg_price":null,
        #        "order_class":"",
        #        "order_type":"limit",
        #        "type":"limit",
        #        "side":"buy",
        #        "time_in_force":"day",
        #        "limit_price":"14000",
        #        "stop_price":null,
        #        "status":"accepted",
        #        "extended_hours":false,
        #        "legs":null,
        #        "trail_percent":null,
        #        "trail_price":null,
        #        "hwm":null,
        #        "commission":"0.42",
        #        "source":null
        #    }
        #
        marketId = self.safe_string(order, 'symbol')
        market = self.safe_market(marketId, market)
        symbol = market['symbol']
        alpacaStatus = self.safe_string(order, 'status')
        status = self.parse_order_status(alpacaStatus)
        feeValue = self.safe_string(order, 'commission')
        fee = None
        if feeValue is not None:
            fee = {
                'cost': feeValue,
                'currency': 'USD',
            }
        orderType = self.safe_string(order, 'order_type')
        if orderType is not None:
            if orderType.find('limit') >= 0:
                # might be limit or stop-limit
                orderType = 'limit'
        datetime = self.safe_string(order, 'submitted_at')
        timestamp = self.parse8601(datetime)
        return self.safe_order({
            'id': self.safe_string(order, 'id'),
            'clientOrderId': self.safe_string(order, 'client_order_id'),
            'timestamp': timestamp,
            'datetime': datetime,
            'lastTradeTimeStamp': None,
            'status': status,
            'symbol': symbol,
            'type': orderType,
            'timeInForce': self.parse_time_in_force(self.safe_string(order, 'time_in_force')),
            'postOnly': None,
            'side': self.safe_string(order, 'side'),
            'price': self.safe_number(order, 'limit_price'),
            'stopPrice': self.safe_number(order, 'stop_price'),
            'triggerPrice': self.safe_number(order, 'stop_price'),
            'cost': None,
            'average': self.safe_number(order, 'filled_avg_price'),
            'amount': self.safe_number(order, 'qty'),
            'filled': self.safe_number(order, 'filled_qty'),
            'remaining': None,
            'trades': None,
            'fee': fee,
            'info': order,
        }, market)

    def parse_order_status(self, status: Str):
        statuses: dict = {
            'pending_new': 'open',
            'accepted': 'open',
            'new': 'open',
            'partially_filled': 'open',
            'activated': 'open',
            'filled': 'closed',
        }
        return self.safe_string(statuses, status, status)

    def parse_time_in_force(self, timeInForce: Str):
        timeInForces: dict = {
            'day': 'Day',
        }
        return self.safe_string(timeInForces, timeInForce, timeInForce)

    def parse_trade(self, trade: dict, market: Market = None) -> Trade:
        #
        #   {
        #       "t":"2022-06-14T05:00:00.027869Z",
        #       "x":"CBSE",
        #       "p":"21942.15",
        #       "s":"0.0001",
        #       "tks":"S",
        #       "i":"355681339"
        #   }
        #
        marketId = self.safe_string(trade, 'S')
        symbol = self.safe_symbol(marketId, market)
        datetime = self.safe_string(trade, 't')
        timestamp = self.parse8601(datetime)
        alpacaSide = self.safe_string(trade, 'tks')
        side: str
        if alpacaSide == 'B':
            side = 'buy'
        elif alpacaSide == 'S':
            side = 'sell'
        priceString = self.safe_string(trade, 'p')
        amountString = self.safe_string(trade, 's')
        return self.safe_trade({
            'info': trade,
            'id': self.safe_string(trade, 'i'),
            'timestamp': timestamp,
            'datetime': self.iso8601(timestamp),
            'symbol': symbol,
            'order': None,
            'type': None,
            'side': side,
            'takerOrMaker': 'taker',
            'price': priceString,
            'amount': amountString,
            'cost': None,
            'fee': None,
        }, market)

    def sign(self, path, api='public', method='GET', params={}, headers=None, body=None):
        endpoint = '/' + self.implode_params(path, params)
        url = self.implode_hostname(self.urls['api'][api[0]])
        headers = headers if (headers is not None) else {}
        if api[1] == 'private':
            headers['APCA-API-KEY-ID'] = self.apiKey
            headers['APCA-API-SECRET-KEY'] = self.secret
        query = self.omit(params, self.extract_params(path))
        if query:
            if (method == 'GET') or (method == 'DELETE'):
                endpoint += '?' + self.urlencode(query)
            else:
                body = self.json(query)
                headers['Content-Type'] = 'application/json'
        url = url + endpoint
        return {'url': url, 'method': method, 'body': body, 'headers': headers}

    def handle_errors(self, code: int, reason: str, url: str, method: str, headers: dict, body: str, response, requestHeaders, requestBody):
        if response is None:
            return None  # default error handler
        # {
        #     "code": 40110000,
        #     "message": "request is not authorized"
        # }
        feedback = self.id + ' ' + body
        errorCode = self.safe_string(response, 'code')
        if code is not None:
            self.throw_exactly_matched_exception(self.exceptions['exact'], errorCode, feedback)
        message = self.safe_value(response, 'message', None)
        if message is not None:
            self.throw_exactly_matched_exception(self.exceptions['exact'], message, feedback)
            self.throw_broadly_matched_exception(self.exceptions['broad'], message, feedback)
            raise ExchangeError(feedback)
        return None
