import asyncio
import json
import re
from copy import deepcopy
from decimal import Decimal
from typing import Any, Callable, List, Optional, Tuple
from unittest.mock import AsyncMock, patch

import pandas as pd
from aioresponses import aioresponses
from aioresponses.core import RequestCall

import hummingbot.connector.derivative.kucoin_perpetual.kucoin_perpetual_constants as CONSTANTS
import hummingbot.connector.derivative.kucoin_perpetual.kucoin_perpetual_web_utils as web_utils
from hummingbot.client.config.client_config_map import ClientConfigMap
from hummingbot.client.config.config_helpers import ClientConfigAdapter
from hummingbot.connector.derivative.kucoin_perpetual.kucoin_perpetual_derivative import KucoinPerpetualDerivative
from hummingbot.connector.derivative.position import Position
from hummingbot.connector.test_support.perpetual_derivative_test import AbstractPerpetualDerivativeTests
from hummingbot.connector.trading_rule import TradingRule
from hummingbot.connector.utils import combine_to_hb_trading_pair
from hummingbot.core.data_type.common import OrderType, PositionAction, PositionMode, PositionSide, TradeType
from hummingbot.core.data_type.funding_info import FundingInfo
from hummingbot.core.data_type.in_flight_order import InFlightOrder
from hummingbot.core.data_type.trade_fee import AddedToCostTradeFee, TokenAmount, TradeFeeBase

class KucoinPerpetualDerivativeTests(AbstractPerpetualDerivativeTests.PerpetualDerivativeTests):
    @classmethod
    def setUpClass(cls) -> None:
        super().setUpClass()
        cls.api_key = "someKey"
        cls.api_secret = "someSecret"
        cls.passphrase = "somePassphrase"
        cls.quote_asset = "USDT"
        cls.trading_pair = combine_to_hb_trading_pair(cls.base_asset, cls.quote_asset)
        cls.non_linear_quote_asset = "USD"
        cls.non_linear_trading_pair = combine_to_hb_trading_pair(cls.base_asset, cls.non_linear_quote_asset)

    @property
    def all_symbols_url(self):
        url = web_utils.get_rest_url_for_endpoint(endpoint=CONSTANTS.QUERY_SYMBOL_ENDPOINT)
        return url

    @property
    def latest_prices_url(self):
        url = web_utils.get_rest_url_for_endpoint(
            endpoint=CONSTANTS.LATEST_SYMBOL_INFORMATION_ENDPOINT.format(symbol=self.exchange_trading_pair),
        )
        url = re.compile(f"^{url}".replace(".", r"\.").replace("?", r"\?") + ".*")
        return url

    @property
    def network_status_url(self):
        url = web_utils.get_rest_url_for_endpoint(endpoint=CONSTANTS.SERVER_TIME_PATH_URL)
        return url

    @property
    def trading_rules_url(self):
        url = web_utils.get_rest_url_for_endpoint(endpoint=CONSTANTS.QUERY_SYMBOL_ENDPOINT)
        return url

    @property
    def order_creation_url(self):
        url = web_utils.get_rest_url_for_endpoint(
            endpoint=CONSTANTS.CREATE_ORDER_PATH_URL
        )
        url = re.compile(f"^{url}".replace(".", r"\.").replace("?", r"\?") + ".*")
        return url

    @property
    def balance_url(self):
        url = web_utils.get_rest_url_for_endpoint(endpoint=CONSTANTS.GET_WALLET_BALANCE_PATH_URL.format(currency="USDT"))
        return url

    @property
    def funding_info_url(self):
        url = web_utils.get_rest_url_for_endpoint(
            endpoint=CONSTANTS.GET_CONTRACT_INFO_PATH_URL
        )
        url = re.compile(f"^{url}".replace(".", r"\.").replace("?", r"\?") + ".*")
        return url

    @property
    def funding_payment_url(self):
        url = web_utils.get_rest_url_for_endpoint(
            endpoint=CONSTANTS.GET_FUNDING_HISTORY_PATH_URL.format(symbol=self.exchange_trading_pair),
        )
        url = re.compile(f"^{url}".replace(".", r"\.").replace("?", r"\?") + ".*")
        return url

    @property
    def all_symbols_request_mock_response(self):
        mock_response = {
            "code": "200000",
            "data": [
                {
                    "symbol": self.exchange_trading_pair,
                    "rootSymbol": self.quote_asset,
                    "type": "FFWCSX",
                    "firstOpenDate": 1585555200000,
                    "expireDate": None,
                    "settleDate": None,
                    "baseCurrency": self.base_asset,
                    "quoteCurrency": self.quote_asset,
                    "settleCurrency": self.quote_asset,
                    "maxOrderQty": 1000000,
                    "maxPrice": 1000000.0,
                    "lotSize": 1,
                    "tickSize": 1.0,
                    "indexPriceTickSize": 0.01,
                    "multiplier": 0.001,
                    "initialMargin": 0.01,
                    "maintainMargin": 0.005,
                    "maxRiskLimit": 2000000,
                    "minRiskLimit": 2000000,
                    "riskStep": 1000000,
                    "makerFeeRate": 0.0002,
                    "takerFeeRate": 0.0006,
                    "takerFixFee": 0.0,
                    "makerFixFee": 0.0,
                    "settlementFee": None,
                    "isDeleverage": True,
                    "isQuanto": True,
                    "isInverse": False,
                    "markMethod": "FairPrice",
                    "fairMethod": "FundingRate",
                    "settlementSymbol": "",
                    "status": "Open",
                    "fundingFeeRate": 0.0001,
                    "predictedFundingFeeRate": 0.0001,
                    "openInterest": "5191275",
                    "turnoverOf24h": 2361994501.712677,
                    "volumeOf24h": 56067.116,
                    "markPrice": 44514.03,
                    "indexPrice": 44510.78,
                    "lastTradePrice": 44493.0,
                    "nextFundingRateTime": 21031525,
                    "maxLeverage": 100,
                    "sourceExchanges": [
                        "huobi",
                        "Okex",
                        "Binance",
                        "Kucoin",
                        "Poloniex",
                        "Hitbtc"
                    ],
                    "lowPrice": 38040,
                    "highPrice": 44948,
                    "priceChgPct": 0.1702,
                    "priceChg": 6476
                }
            ]
        }
        return mock_response

    @property
    def latest_prices_request_mock_response(self):
        mock_response = {
            "code": "200000",
            "data": [
                {
                    "symbol": self.exchange_trading_pair,
                    "rootSymbol": self.quote_asset,
                    "type": "FFWCSX",
                    "firstOpenDate": 1610697600000,
                    "expireDate": None,
                    "settleDate": None,
                    "baseCurrency": self.base_asset,
                    "quoteCurrency": self.quote_asset,
                    "settleCurrency": self.quote_asset,
                    "maxOrderQty": 1000000,
                    "maxPrice": 1000000.0,
                    "lotSize": 1,
                    "tickSize": 0.01,
                    "indexPriceTickSize": 0.01,
                    "multiplier": 0.01,
                    "initialMargin": 0.05,
                    "maintainMargin": 0.025,
                    "maxRiskLimit": 100000,
                    "minRiskLimit": 100000,
                    "riskStep": 50000,
                    "makerFeeRate": 0.0002,
                    "takerFeeRate": 0.0006,
                    "takerFixFee": 0.0,
                    "makerFixFee": 0.0,
                    "settlementFee": "",
                    "isDeleverage": True,
                    "isQuanto": False,
                    "isInverse": False,
                    "markMethod": "FairPrice",
                    "fairMethod": "FundingRate",
                    "fundingBaseSymbol": self.exchange_trading_pair,
                    "fundingQuoteSymbol": self.exchange_trading_pair,
                    "fundingRateSymbol": self.exchange_trading_pair,
                    "indexSymbol": self.exchange_trading_pair,
                    "settlementSymbol": "",
                    "status": "Open",
                    "fundingFeeRate": 0.0001,
                    "predictedFundingFeeRate": 0.0001,
                    "openInterest": "2487402",
                    "turnoverOf24h": 3166644.36115288,
                    "volumeOf24h": 32299.4,
                    "markPrice": 101.6,
                    "indexPrice": 101.59,
                    "lastTradePrice": str(self.expected_latest_price),
                    "nextFundingRateTime": 22646889,
                    "maxLeverage": 20,
                    "sourceExchanges": [
                        "huobi",
                        "Okex",
                        "Binance",
                        "Kucoin",
                        "Poloniex",
                        "Hitbtc"
                    ],
                    "premiumsSymbol1M": self.exchange_trading_pair,
                    "premiumsSymbol8H": self.exchange_trading_pair,
                    "fundingBaseSymbol1M": self.base_asset,
                    "fundingQuoteSymbol1M": self.quote_asset,
                    "lowPrice": 88.88,
                    "highPrice": 102.21,
                    "priceChgPct": 0.1401,
                    "priceChg": 12.48
                }
            ]
        }
        return mock_response

    @property
    def all_symbols_including_invalid_pair_mock_response(self) -> Tuple[str, Any]:
        mock_response = {
            "code": "200000",
            "data": [
                {
                    "symbol": self.exchange_trading_pair,
                    "rootSymbol": self.quote_asset,
                    "type": "FFWCSX",
                    "firstOpenDate": 1585555200000,
                    "expireDate": None,
                    "settleDate": None,
                    "baseCurrency": self.base_asset,
                    "quoteCurrency": self.quote_asset,
                    "settleCurrency": self.quote_asset,
                    "maxOrderQty": 1000000,
                    "maxPrice": 1000000.0,
                    "lotSize": 1,
                    "tickSize": 1.0,
                    "indexPriceTickSize": 0.01,
                    "multiplier": 0.001,
                    "initialMargin": 0.01,
                    "maintainMargin": 0.005,
                    "maxRiskLimit": 2000000,
                    "minRiskLimit": 2000000,
                    "riskStep": 1000000,
                    "makerFeeRate": 0.0002,
                    "takerFeeRate": 0.0006,
                    "takerFixFee": 0.0,
                    "makerFixFee": 0.0,
                    "settlementFee": None,
                    "isDeleverage": True,
                    "isQuanto": True,
                    "isInverse": False,
                    "markMethod": "FairPrice",
                    "fairMethod": "FundingRate",
                    "settlementSymbol": "",
                    "status": "Open",
                    "fundingFeeRate": 0.0001,
                    "predictedFundingFeeRate": 0.0001,
                    "openInterest": "5191275",
                    "turnoverOf24h": 2361994501.712677,
                    "volumeOf24h": 56067.116,
                    "markPrice": 44514.03,
                    "indexPrice": 44510.78,
                    "lastTradePrice": 44493.0,
                    "nextFundingRateTime": 21031525,
                    "maxLeverage": 100,
                    "sourceExchanges": [
                        "huobi",
                        "Okex",
                        "Binance",
                        "Kucoin",
                        "Poloniex",
                        "Hitbtc"
                    ],
                    "lowPrice": 38040,
                    "highPrice": 44948,
                    "priceChgPct": 0.1702,
                    "priceChg": 6476
                },
                {
                    "symbol": self.exchange_symbol_for_tokens("INVALID", "PAIR"),
                    "rootSymbol": self.quote_asset,
                    "type": "FFWCSX",
                    "firstOpenDate": 1585555200000,
                    "expireDate": None,
                    "settleDate": None,
                    "baseCurrency": "INVALID",
                    "quoteCurrency": "PAIR",
                    "settleCurrency": "PAIR",
                    "maxOrderQty": 1000000,
                    "maxPrice": 1000000.0,
                    "lotSize": 1,
                    "tickSize": 1.0,
                    "indexPriceTickSize": 0.01,
                    "multiplier": 0.001,
                    "initialMargin": 0.01,
                    "maintainMargin": 0.005,
                    "maxRiskLimit": 2000000,
                    "minRiskLimit": 2000000,
                    "riskStep": 1000000,
                    "makerFeeRate": 0.0002,
                    "takerFeeRate": 0.0006,
                    "takerFixFee": 0.0,
                    "makerFixFee": 0.0,
                    "settlementFee": None,
                    "isDeleverage": True,
                    "isQuanto": True,
                    "isInverse": False,
                    "markMethod": "FairPrice",
                    "fairMethod": "FundingRate",
                    "settlementSymbol": "",
                    "status": "Closed",
                    "fundingFeeRate": 0.0001,
                    "predictedFundingFeeRate": 0.0001,
                    "openInterest": "5191275",
                    "turnoverOf24h": 2361994501.712677,
                    "volumeOf24h": 56067.116,
                    "markPrice": 44514.03,
                    "indexPrice": 44510.78,
                    "lastTradePrice": 44493.0,
                    "nextFundingRateTime": 21031525,
                    "maxLeverage": 100,
                    "sourceExchanges": [
                        "huobi",
                        "Okex",
                        "Binance",
                        "Kucoin",
                        "Poloniex",
                        "Hitbtc"
                    ],
                    "lowPrice": 38040,
                    "highPrice": 44948,
                    "priceChgPct": 0.1702,
                    "priceChg": 6476
                },
            ]
        }
        return "INVALID-PAIR", mock_response

    @property
    def network_status_request_successful_mock_response(self):
        mock_response = {
            "code": "200000",
            "data": {
                "status": "open",
                "msg": "upgrade match engine"
            }
        }
        return mock_response

    @property
    def trading_rules_request_mock_response(self):
        return self.all_symbols_request_mock_response

    @property
    def trading_rules_request_erroneous_mock_response(self):
        mock_response = {
            "code": "200000",
            "data": [
                {
                    "symbol": self.exchange_trading_pair,
                    "rootSymbol": self.quote_asset,
                    "type": "FFWCSX",
                    "firstOpenDate": 1610697600000,
                    "expireDate": None,
                    "settleDate": None,
                    "baseCurrency": self.base_asset,
                    "quoteCurrency": self.quote_asset,
                    "settleCurrency": self.quote_asset,
                    "makerFeeRate": 0.0002,
                    "takerFeeRate": 0.0006,
                }
            ]
        }
        return mock_response

    @property
    def order_creation_request_successful_mock_response(self):
        mock_response = {
            "code": "200000",
            "data": {
                "orderId": "335fd977-e5a5-4781-b6d0-c772d5bfb95b"
            }
        }
        return mock_response

    @property
    def balance_request_mock_response_for_base_and_quote(self):
        mock_response = {
            "code": "200000",
            "data": [{
                    "accountEquity": 15,
                    "unrealisedPNL": 0,
                    "marginBalance": 15,
                    "positionMargin": 0,
                    "orderMargin": 0,
                    "frozenFunds": 0,
                    "availableBalance": 10,
                    "currency": self.base_asset,
            },
                {
                "accountEquity": 2000,
                    "unrealisedPNL": 0,
                    "marginBalance": 2000,
                    "positionMargin": 0,
                    "orderMargin": 0,
                    "frozenFunds": 0,
                    "availableBalance": 2000,
                    "currency": self.quote_asset,
            }
            ]
        }
        return mock_response

    @property
    def balance_request_mock_response_only_base(self):
        mock_response = self.balance_request_mock_response_for_base_and_quote
        del mock_response["data"][1]
        return mock_response

    @property
    def balance_event_websocket_update(self):
        mock_response = {
            "userId": 738713,
            "topic": "/contractAccount/wallet",
            "subject": "availableBalance.change",
            "data": {
                "availableBalance": 10,
                "holdBalance": 15,
                "currency": self.base_asset,
                "timestamp": 1553842862614
            }
        }
        return mock_response

    @property
    def non_linear_balance_event_websocket_update(self):
        return self.balance_event_websocket_update

    @property
    def expected_latest_price(self):
        return 9999.9

    @property
    def empty_funding_payment_mock_response(self):
        return {
            "code": "200000",
            "dataList": [{}],
        }

    @property
    def funding_payment_mock_response(self):
        return {
            "code": "200000",
            "dataList": [
                {
                    "id": 36275152660006,
                    "symbol": self.exchange_trading_pair,
                    "timePoint": self.target_funding_payment_timestamp_str,
                    "fundingRate": float(self.target_funding_payment_funding_rate),
                    "markPrice": 8058.27,
                    "positionQty": float(self.target_funding_payment_payment_amount / self.target_funding_payment_funding_rate),
                    "positionCost": -0.001241,
                    "funding": -0.00000464,
                    "settleCurrency": self.base_asset,
                }]
        }

    @property
    def expected_supported_position_modes(self) -> List[PositionMode]:
        raise NotImplementedError  # test is overwritten

    @property
    def target_funding_info_next_funding_utc_str(self):
        datetime_str = str(
            pd.Timestamp.utcfromtimestamp(
                self.target_funding_info_next_funding_utc_timestamp)
        ).replace(" ", "T") + "Z"
        return datetime_str

    @property
    def target_funding_info_next_funding_utc_str_ws_updated(self):
        datetime_str = str(
            pd.Timestamp.utcfromtimestamp(
                self.target_funding_info_next_funding_utc_timestamp_ws_updated)
        ).replace(" ", "T") + "Z"
        return datetime_str

    @property
    def target_funding_payment_timestamp_str(self):
        datetime_str = str(
            pd.Timestamp.utcfromtimestamp(
                self.target_funding_payment_timestamp)
        ).replace(" ", "T") + "Z"
        return datetime_str

    @property
    def funding_info_mock_response(self):
        mock_response = self.latest_prices_request_mock_response
        funding_info = mock_response["data"][0]
        funding_info["indexPrice"] = self.target_funding_info_index_price
        funding_info["markPrice"] = self.target_funding_info_mark_price
        funding_info["nextFundingRateTime"] = self.target_funding_info_next_funding_utc_str
        funding_info["predictedFundingFeeRate"] = self.target_funding_info_rate
        return mock_response

    @property
    def get_predicted_funding_info(self):
        return self.latest_prices_request_mock_response

    @property
    def expected_supported_order_types(self):
        return [OrderType.LIMIT, OrderType.MARKET, OrderType.LIMIT_MAKER]

    @property
    def expected_trading_rule(self):
        trading_rules_resp = self.trading_rules_request_mock_response["data"][0]
        multiplier = Decimal(str(trading_rules_resp["multiplier"]))
        return TradingRule(
            trading_pair=self.trading_pair,
            min_order_size=Decimal(str(trading_rules_resp["lotSize"])) * multiplier,
            max_order_size=Decimal(str(trading_rules_resp["maxOrderQty"])) * multiplier,
            min_price_increment=Decimal(str(trading_rules_resp["tickSize"])),
            min_base_amount_increment=multiplier,
        )

    @property
    def expected_logged_error_for_erroneous_trading_rule(self):
        erroneous_rule = self.trading_rules_request_erroneous_mock_response["data"][0]
        return f"Error parsing the trading pair rule: {erroneous_rule}. Skipping..."

    @property
    def expected_exchange_order_id(self):
        return "335fd977-e5a5-4781-b6d0-c772d5bfb95b"

    @property
    def is_cancel_request_executed_synchronously_by_server(self) -> bool:
        return False

    @property
    def is_order_fill_http_update_included_in_status_update(self) -> bool:
        return False

    @property
    def is_order_fill_http_update_executed_during_websocket_order_event_processing(self) -> bool:
        return False

    @property
    def expected_partial_fill_price(self) -> Decimal:
        return Decimal("100")

    @property
    def expected_partial_fill_amount(self) -> Decimal:
        return Decimal("10")

    @property
    def expected_fill_fee(self) -> TradeFeeBase:
        return AddedToCostTradeFee(
            percent=Decimal('0.0002'),
            percent_token=self.quote_asset,
        )

    @property
    def expected_trade_history_fill_fee(self) -> TradeFeeBase:
        return AddedToCostTradeFee(
            percent=Decimal('0'),
            percent_token=self.quote_asset,
            flat_fees=[TokenAmount(amount=Decimal('0.0002'), token=self.quote_asset)]
        )

    @property
    def expected_fill_trade_id(self) -> str:
        return "xxxxxxxx-xxxx-xxxx-8b66-c3d2fcd352f6"

    @property
    def latest_trade_hist_timestamp(self) -> int:
        return 1234

    def exchange_symbol_for_tokens(self, base_token: str, quote_token: str) -> str:
        return f"{base_token}{quote_token}"

    def create_exchange_instance(self):
        client_config_map = ClientConfigAdapter(ClientConfigMap())
        exchange = KucoinPerpetualDerivative(
            client_config_map,
            self.api_key,
            self.api_secret,
            self.passphrase,
            trading_pairs=[self.trading_pair],
        )
        exchange._last_trade_history_timestamp = self.latest_trade_hist_timestamp
        return exchange

    def validate_auth_credentials_present(self, request_call: RequestCall):
        request_headers = request_call.kwargs["headers"]
        self.assertEqual("application/json", request_headers["Content-Type"])

        self.assertIn("KC-API-TIMESTAMP", request_headers)
        self.assertIn("KC-API-KEY", request_headers)
        self.assertEqual(self.api_key, request_headers["KC-API-KEY"])
        self.assertIn("KC-API-SIGN", request_headers)

    def validate_order_creation_request(self, order: InFlightOrder, request_call: RequestCall):
        request_data = json.loads(request_call.kwargs["data"])
        self.assertEqual(order.trade_type.name.lower(), request_data["side"])
        self.assertEqual(self.exchange_trading_pair, request_data["symbol"])
        self.assertEqual(order.amount, request_data["size"] * 1e-6)
        self.assertEqual(CONSTANTS.DEFAULT_TIME_IN_FORCE, request_data["timeInForce"])
        self.assertEqual(order.client_order_id, request_data["clientOid"])
        self.assertIn("clientOid", request_data)
        self.assertEqual(order.order_type.name.lower(), request_data["type"])

    def validate_order_cancelation_request(self, order: InFlightOrder, request_call: RequestCall):
        request_data = json.loads(request_call.kwargs["data"])
        self.assertEqual(order.exchange_order_id, request_data["order_id"])

    def validate_order_status_request(self, order: InFlightOrder, request_call: RequestCall):
        request_params = request_call.kwargs["params"]
        request_data = request_call.kwargs["data"]
        self.assertIsNone(request_params)
        self.assertIsNone(request_data)

    def validate_trades_request(self, order: InFlightOrder, request_call: RequestCall):
        request_params = request_call.kwargs["params"]
        self.assertEqual(self.exchange_trading_pair, request_params["symbol"])
        self.assertEqual(self.latest_trade_hist_timestamp * 1e3, request_params["start_time"])

    def configure_successful_cancelation_response(
        self,
        order: InFlightOrder,
        mock_api: aioresponses,
        callback: Optional[Callable] = lambda *args, **kwargs: None,
    ) -> str:
        """
        :return: the URL configured for the cancelation
        """
        url = web_utils.get_rest_url_for_endpoint(
            endpoint=CONSTANTS.CANCEL_ORDER_PATH_URL.format(orderid=order.exchange_order_id)
        )
        regex_url = re.compile(f"^{url}".replace(".", r"\.").replace("?", r"\?") + ".*")
        response = self._order_cancelation_request_successful_mock_response(order=order)
        mock_api.delete(regex_url, body=json.dumps(response), callback=callback)
        return url

    def configure_erroneous_cancelation_response(
        self,
        order: InFlightOrder,
        mock_api: aioresponses,
        callback: Optional[Callable] = lambda *args, **kwargs: None,
    ) -> str:
        url = web_utils.get_rest_url_for_endpoint(
            endpoint=CONSTANTS.CANCEL_ORDER_PATH_URL.format(orderid=order.exchange_order_id)
        )
        regex_url = re.compile(f"^{url}".replace(".", r"\.").replace("?", r"\?") + ".*")
        response = {
            "code": str(CONSTANTS.RET_CODE_PARAMS_ERROR),
            "msg": "Order does not exist",
        }
        mock_api.delete(regex_url, body=json.dumps(response), callback=callback)
        return url

    def configure_one_successful_one_erroneous_cancel_all_response(
        self,
        successful_order: InFlightOrder,
        erroneous_order: InFlightOrder,
        mock_api: aioresponses,
    ) -> List[str]:
        """
        :return: a list of all configured URLs for the cancelations
        """
        all_urls = []
        url = self.configure_successful_cancelation_response(order=successful_order, mock_api=mock_api)
        all_urls.append(url)
        url = self.configure_erroneous_cancelation_response(order=erroneous_order, mock_api=mock_api)
        all_urls.append(url)
        return all_urls

    def configure_completely_filled_order_status_response(
        self,
        order: InFlightOrder,
        mock_api: aioresponses,
        callback: Optional[Callable] = lambda *args, **kwargs: None
    ) -> str:
        url = web_utils.get_rest_url_for_endpoint(endpoint=CONSTANTS.QUERY_ORDER_BY_EXCHANGE_ORDER_ID_PATH_URL.format(orderid=order.exchange_order_id))
        response = self._order_status_request_completely_filled_mock_response(order=order)
        mock_api.get(url, body=json.dumps(response), callback=callback)
        return url

    def configure_canceled_order_status_response(
        self,
        order: InFlightOrder,
        mock_api: aioresponses,
        callback: Optional[Callable] = lambda *args, **kwargs: None,
    ) -> str:
        url = web_utils.get_rest_url_for_endpoint(
            endpoint=CONSTANTS.QUERY_ORDER_BY_EXCHANGE_ORDER_ID_PATH_URL.format(orderid=order.exchange_order_id)
        )
        response = self._order_status_request_canceled_mock_response(order=order)
        mock_api.get(url, body=json.dumps(response), callback=callback)
        return url

    def configure_open_order_status_response(
        self,
        order: InFlightOrder,
        mock_api: aioresponses,
        callback: Optional[Callable] = lambda *args, **kwargs: None,
    ) -> str:
        url = web_utils.get_rest_url_for_endpoint(
            endpoint=CONSTANTS.QUERY_ORDER_BY_EXCHANGE_ORDER_ID_PATH_URL.format(orderid=order.exchange_order_id)
        )
        regex_url = re.compile(url + r"\?.*")
        response = self._order_status_request_open_mock_response(order=order)
        mock_api.get(regex_url, body=json.dumps(response), callback=callback)
        return url

    def configure_http_error_order_status_response(
        self,
        order: InFlightOrder,
        mock_api: aioresponses,
        callback: Optional[Callable] = lambda *args, **kwargs: None,
    ) -> str:
        url = web_utils.get_rest_url_for_endpoint(
            endpoint=CONSTANTS.QUERY_ORDER_BY_EXCHANGE_ORDER_ID_PATH_URL.format(orderid=order.exchange_order_id)
        )
        regex_url = re.compile(url + r"\?.*")
        mock_api.get(regex_url, status=404, callback=callback)
        return url

    def configure_partially_filled_order_status_response(
        self,
        order: InFlightOrder,
        mock_api: aioresponses,
        callback: Optional[Callable] = lambda *args, **kwargs: None,
    ) -> str:
        url = web_utils.get_rest_url_for_endpoint(
            endpoint=CONSTANTS.QUERY_ORDER_BY_EXCHANGE_ORDER_ID_PATH_URL.format(orderid=order.exchange_order_id)
        )
        response = self._order_status_request_partially_filled_mock_response(order=order)
        mock_api.get(url, body=json.dumps(response), callback=callback)
        return url

    def configure_partial_fill_trade_response(
        self,
        order: InFlightOrder,
        mock_api: aioresponses,
        callback: Optional[Callable] = lambda *args, **kwargs: None,
    ) -> str:
        url = web_utils.get_rest_url_for_endpoint(
            endpoint=CONSTANTS.QUERY_ALL_ORDER_PATH_URL, exchange_order_id=order.exchange_order_id
        )
        regex_url = re.compile(url + r"\?.*")
        response = self._order_fills_request_partial_fill_mock_response(order=order)
        mock_api.get(regex_url, body=json.dumps(response), callback=callback)
        return url

    def configure_full_fill_trade_response(
        self,
        order: InFlightOrder,
        mock_api: aioresponses,
        callback: Optional[Callable] = lambda *args, **kwargs: None,
    ) -> str:
        url = web_utils.get_rest_url_for_endpoint(
            endpoint=CONSTANTS.GET_FILL_INFO_PATH_URL.format(orderid=order.exchange_order_id),
        )
        response = self._order_fills_request_full_fill_mock_response(order=order)
        mock_api.get(url, body=json.dumps(response), callback=callback)
        return url

    def configure_fill_history_trade_response(
        self,
        order: InFlightOrder,
        mock_api: aioresponses,
        callback: Optional[Callable] = lambda *args, **kwargs: None,
    ) -> str:
        url = web_utils.get_rest_url_for_endpoint(
            endpoint=CONSTANTS.GET_RECENT_FILLS_INFO_PATH_URL,
        )
        response = self._order_fills_request_full_fill_mock_response(order=order)
        mock_api.get(url, body=json.dumps(response), callback=callback)
        return url

    def configure_erroneous_http_fill_trade_response(
        self,
        order: InFlightOrder,
        mock_api: aioresponses,
        callback: Optional[Callable] = lambda *args, **kwargs: None,
    ) -> str:
        url = web_utils.get_rest_url_for_endpoint(
            endpoint=CONSTANTS.ACTIVE_ORDER_PATH_URL, exchange_order_id=order.exchange_order_id
        )
        regex_url = re.compile(url + r"\?.*")
        mock_api.get(regex_url, status=400, callback=callback)
        return url

    def configure_successful_set_position_mode(
        self,
        position_mode: PositionMode,
        mock_api: aioresponses,
        callback: Optional[Callable] = lambda *args, **kwargs: None,
    ):
        url = web_utils.get_rest_url_for_endpoint(
            endpoint=CONSTANTS.SET_LEVERAGE_PATH_URL
        )
        response = {
            "code": "200000",
            "data": True
        }
        mock_api.post(url, body=json.dumps(response), callback=callback)

        return url

    def configure_failed_set_position_mode(
        self,
        position_mode: PositionMode,
        mock_api: aioresponses,
        callback: Optional[Callable] = lambda *args, **kwargs: None
    ):
        url = web_utils.get_rest_url_for_endpoint(
            endpoint=CONSTANTS.SET_LEVERAGE_PATH_URL
        )
        error_code = "300016"
        error_msg = "Some problem"
        response = {
            "code": "300016",
            "data": False
        }
        mock_api.post(url, body=json.dumps(response), callback=callback)

        return url, f"ret_code <{error_code}> - {error_msg}"

    def configure_failed_set_leverage(
        self,
        leverage: PositionMode,
        mock_api: aioresponses,
        callback: Optional[Callable] = lambda *args, **kwargs: None,
    ) -> Tuple[str, str]:
        url = web_utils.get_rest_url_for_endpoint(
            endpoint=CONSTANTS.SET_LEVERAGE_PATH_URL
        )
        regex_url = re.compile(f"^{url}")

        error_code = "300016"
        error_msg = "Some problem"
        mock_response = {
            "code": "300016",
            "data": False
        }

        mock_api.post(regex_url, body=json.dumps(mock_response), callback=callback)

        return url, f"ret_code <{error_code}> - {error_msg}"

    def configure_successful_set_leverage(
        self,
        leverage: int,
        mock_api: aioresponses,
        callback: Optional[Callable] = lambda *args, **kwargs: None,
    ):
        url = web_utils.get_rest_url_for_endpoint(
            endpoint=CONSTANTS.SET_LEVERAGE_PATH_URL
        )
        regex_url = re.compile(f"^{url}")

        mock_response = {
            "code": "200000",
            "data": True
        }

        mock_api.post(regex_url, body=json.dumps(mock_response), callback=callback)

        return url

    def order_event_for_new_order_websocket_update(self, order: InFlightOrder):
        return {
            "type": "message",
            "topic": "/contractMarket/tradeOrders",
            "subject": "orderChange",
            "channelType": "private",
            "data": {
                "orderId": order.exchange_order_id or "1640b725-75e9-407d-bea9-aae4fc666d33",
                "symbol": self.exchange_trading_pair,
                "type": "open",
                "status": "open",
                "orderType": order.order_type.name.lower(),
                "side": order.trade_type.name.lower(),
                "price": str(order.price),
                "size": float(order.amount),
                "remainSize": float(order.amount),
                "filledSize": "0",
                "canceledSize": "0",
                "clientOid": order.client_order_id or "",
                "orderTime": 1545914149935808589,
                "liquidity": "maker",
                "ts": 1545914149935808589
            }
        }

    def order_event_for_canceled_order_websocket_update(self, order: InFlightOrder):
        return {
            "type": "message",
            "topic": "/contractMarket/tradeOrders",
            "subject": "orderChange",
            "channelType": "private",
            "data": {
                "orderId": order.exchange_order_id or "1640b725-75e9-407d-bea9-aae4fc666d33",
                "symbol": self.exchange_trading_pair,
                "type": "canceled",
                "status": "done",
                "orderType": order.order_type.name.lower(),
                "side": order.trade_type.name.lower(),
                "price": str(order.price),
                "size": float(order.amount),
                "remainSize": "0",
                "filledSize": "0",
                "canceledSize": float(order.amount),
                "clientOid": order.client_order_id or "",
                "orderTime": 1545914149935808589,
                "liquidity": "maker",
                "ts": 1545914149935808589
            }
        }

    def order_event_for_full_fill_websocket_update(self, order: InFlightOrder):
        return {
            "type": "message",
            "topic": "/contractMarket/tradeOrders",
            "subject": "orderChange",
            "channelType": "private",
            "data": {
                "orderId": order.exchange_order_id or "1640b725-75e9-407d-bea9-aae4fc666d33",
                "symbol": self.exchange_trading_pair,
                "type": "filled",
                "status": "done",
                "orderType": order.order_type.name.lower(),
                "side": order.trade_type.name.lower(),
                "matchPrice": str(order.price),
                "size": float(order.amount) * 1000,
                "remainSize": "0",
                "matchSize": float(order.amount) * 1000,
                "fee": str(self.expected_fill_fee.percent),
                "canceledSize": "0",
                "clientOid": order.client_order_id or "",
                "orderTime": 1545914149935808589,
                "liquidity": "maker",
                "ts": 1545914149935808589
            }
        }

    def trade_event_for_full_fill_websocket_update(self, order: InFlightOrder):
        return {
            "type": "message",
            "topic": "/contractMarket/tradeOrders",
            "subject": "orderChange",
            "channelType": "private",
            "data": {
                "orderId": order.exchange_order_id or "1640b725-75e9-407d-bea9-aae4fc666d33",
                "tradeId": self.expected_fill_trade_id,
                "symbol": self.exchange_trading_pair,
                "type": "match",
                "status": "done",
                "orderType": order.order_type.name.lower(),
                "side": order.trade_type.name.lower(),
                "matchPrice": str(order.price),
                "size": float(order.amount) * 1000,
                "fee": str(self.expected_fill_fee.percent),
                "remainSize": "0",
                "matchSize": float(order.amount) * 1000000,
                "canceledSize": "0",
                "clientOid": order.client_order_id or "",
                "orderTime": 1545914149935808589,
                "liquidity": "maker",
                "ts": 1545914149935808589
            }
        }

    def position_event_for_full_fill_websocket_update(self, order: InFlightOrder, unrealized_pnl: float):
        position_value = unrealized_pnl + order.amount * order.price * order.leverage
        return {
            "type": "message",
            "userId": 533285,
            "channelType": "private",
            "topic": "/contract/position:" + self.exchange_trading_pair,
            "subject": "position.change",
            "data": {
                "realisedGrossPnl": "0.00055631",
                "symbol": self.exchange_trading_pair,
                "crossMode": False,
                "liquidationPrice": "489",
                "posLoss": 0E-8,
                "avgEntryPrice": str(order.price),
                "unrealisedPnl": unrealized_pnl,
                "markPrice": str(order.price),
                "posMargin": 0.00266779,
                "autoDeposit": False,
                "riskLimit": 100000,
                "unrealisedCost": 0.00266375,
                "posComm": 0.00000392,
                "posMaint": 0.00001724,
                "posCost": str(position_value),
                "maintMarginReq": 0.005,
                "bankruptPrice": 1000000.0,
                "realisedCost": 0.00000271,
                "markValue": 0.00251640,
                "posInit": 0.39929535,
                "realisedPnl": -0.00000253,
                "maintMargin": 0.39929535,
                "realLeverage": str(order.leverage),
                "changeReason": "positionChange",
                "currentCost": str(position_value),
                "openingTimestamp": 1558433191000,
                "currentQty": -int(order.amount),
                "delevPercentage": 0.52,
                "currentComm": 0.00000271,
                "realisedGrossCost": 0E-8,
                "isOpen": True,
                "posCross": 1.2E-7,
                "currentTimestamp": 1558506060394,
                "unrealisedRoePcnt": -0.0553,
                "unrealisedPnlPcnt": -0.0553,
                "settleCurrency": self.quote_asset,
            }
        }

    def funding_info_event_for_websocket_update(self):
        return {
            "userId": "xbc453tg732eba53a88ggyt8c",  # Deprecated, will detele later
            "topic": "/contract/position:" + self.exchange_trading_pair,
            "subject": "position.settlement",
            "data": {
                "fundingTime": 1551770400000,         # Funding time
                "qty": 100,                           # Position size
                "markPrice": self.target_funding_info_mark_price_ws_updated,  # Settlement price
                "fundingRate": self.target_funding_info_rate_ws_updated,             # Funding rate
                "fundingFee": -296,                   # Funding fees
                "ts": 1547697294838004923,            # Current time (nanosecond)
                "settleCurrency": "XBT"               # Currency used to clear and settle the trades
            }
        }

    def test_create_order_with_invalid_position_action_raises_value_error(self):
        self._simulate_trading_rules_initialized()

        with self.assertRaises(ValueError) as exception_context:
            asyncio.get_event_loop().run_until_complete(
                self.exchange._create_order(
                    trade_type=TradeType.BUY,
                    order_id="C1",
                    trading_pair=self.trading_pair,
                    amount=Decimal("1"),
                    order_type=OrderType.LIMIT,
                    price=Decimal("46000"),
                    position_action=PositionAction.NIL,
                ),
            )

        self.assertEqual(
            f"Invalid position action {PositionAction.NIL}. Must be one of {[PositionAction.OPEN, PositionAction.CLOSE]}",
            str(exception_context.exception)
        )

    def test_user_stream_balance_update(self):
        client_config_map = ClientConfigAdapter(ClientConfigMap())
        non_linear_connector = KucoinPerpetualDerivative(
            client_config_map=client_config_map,
            kucoin_perpetual_api_key=self.api_key,
            kucoin_perpetual_secret_key=self.api_secret,
            trading_pairs=[self.base_asset],
        )
        non_linear_connector._set_current_timestamp(1640780000)

        balance_event = self.non_linear_balance_event_websocket_update

        mock_queue = AsyncMock()
        mock_queue.get.side_effect = [balance_event, asyncio.CancelledError]
        self.exchange._user_stream_tracker._user_stream = mock_queue

        try:
            self.async_run_with_timeout(self.exchange._user_stream_event_listener())
        except asyncio.CancelledError:
            pass

        self.assertEqual(Decimal("10"), self.exchange.available_balances[self.base_asset])
        self.assertEqual(Decimal("25"), self.exchange.get_balance(self.base_asset))

    def test_supported_position_modes(self):
        client_config_map = ClientConfigAdapter(ClientConfigMap())
        linear_connector = KucoinPerpetualDerivative(
            client_config_map=client_config_map,
            kucoin_perpetual_api_key=self.api_key,
            kucoin_perpetual_secret_key=self.api_secret,
            trading_pairs=[self.trading_pair],
        )
        non_linear_connector = KucoinPerpetualDerivative(
            client_config_map=client_config_map,
            kucoin_perpetual_api_key=self.api_key,
            kucoin_perpetual_secret_key=self.api_secret,
            trading_pairs=[self.non_linear_trading_pair],
        )

        expected_result = [PositionMode.ONEWAY]
        self.assertEqual(expected_result, linear_connector.supported_position_modes())

        expected_result = [PositionMode.ONEWAY]
        self.assertEqual(expected_result, non_linear_connector.supported_position_modes())

    def test_set_position_mode_nonlinear(self):
        client_config_map = ClientConfigAdapter(ClientConfigMap())
        non_linear_connector = KucoinPerpetualDerivative(
            client_config_map=client_config_map,
            kucoin_perpetual_api_key=self.api_key,
            kucoin_perpetual_secret_key=self.api_secret,
            trading_pairs=[self.non_linear_trading_pair],
        )
        non_linear_connector.set_position_mode(PositionMode.HEDGE)

        self.assertTrue(
            self.is_logged(
                log_level="ERROR",
                message=f"Position mode {PositionMode.HEDGE} is not supported. Mode not set.",
            )
        )

    def test_get_buy_and_sell_collateral_tokens(self):
        self._simulate_trading_rules_initialized()

        linear_buy_collateral_token = self.exchange.get_buy_collateral_token(self.trading_pair)
        linear_sell_collateral_token = self.exchange.get_sell_collateral_token(self.trading_pair)

        self.assertEqual(self.quote_asset, linear_buy_collateral_token)
        self.assertEqual(self.quote_asset, linear_sell_collateral_token)

        non_linear_buy_collateral_token = self.exchange.get_buy_collateral_token(self.non_linear_trading_pair)
        non_linear_sell_collateral_token = self.exchange.get_sell_collateral_token(self.non_linear_trading_pair)

        self.assertEqual(self.non_linear_quote_asset, non_linear_buy_collateral_token)
        self.assertEqual(self.non_linear_quote_asset, non_linear_sell_collateral_token)

    def test_time_synchronizer_related_reqeust_error_detection(self):
        error_code_str = self.exchange._format_ret_code_for_print(ret_code=CONSTANTS.RET_CODE_AUTH_TIMESTAMP_ERROR)
        exception = IOError(f"{error_code_str} - Failed to cancel order for timestamp reason.")
        self.assertTrue(self.exchange._is_request_exception_related_to_time_synchronizer(exception))

        error_code_str = self.exchange._format_ret_code_for_print(ret_code=CONSTANTS.RET_CODE_ORDER_NOT_EXISTS)
        exception = IOError(f"{error_code_str} - Failed to cancel order because it was not found.")
        self.assertFalse(self.exchange._is_request_exception_related_to_time_synchronizer(exception))

    def place_buy_limit_maker_order(
        self,
        amount: Decimal = Decimal("100"),
        price: Decimal = Decimal("10_000"),
        position_action: PositionAction = PositionAction.OPEN,
    ):
        order_id = self.exchange.buy(
            trading_pair=self.trading_pair,
            amount=amount,
            order_type=OrderType.LIMIT_MAKER,
            price=price,
            position_action=position_action,
        )
        return order_id

    def place_buy_market_order(
        self,
        amount: Decimal = Decimal("100"),
        price: Decimal = Decimal("10_000"),
        position_action: PositionAction = PositionAction.OPEN,
    ):
        order_id = self.exchange.buy(
            trading_pair=self.trading_pair,
            amount=amount,
            order_type=OrderType.MARKET,
            price=price,
            position_action=position_action,
        )
        return order_id

    @aioresponses()
    @patch("asyncio.Queue.get")
    def test_listen_for_funding_info_update_initializes_funding_info(self, mock_api, mock_queue_get):
        url = self.funding_info_url

        response = self.funding_info_mock_response

        url = web_utils.get_rest_url_for_endpoint(endpoint=CONSTANTS.GET_CONTRACT_INFO_PATH_URL.format(symbol=self.exchange_trading_pair))
        regex_url = re.compile(f"^{url}".replace(".", r"\.").replace("?", r"\?"))
        mock_api.get(regex_url, body=json.dumps(response))

        event_messages = [asyncio.CancelledError]
        mock_queue_get.side_effect = event_messages

        try:
            self.async_run_with_timeout(self.exchange._listen_for_funding_info())
        except asyncio.CancelledError:
            pass

        funding_info: FundingInfo = self.exchange.get_funding_info(self.trading_pair)

        self.assertEqual(self.trading_pair, funding_info.trading_pair)
        self.assertEqual(self.target_funding_info_index_price, funding_info.index_price)
        self.assertEqual(self.target_funding_info_mark_price, funding_info.mark_price)
        self.assertEqual(self.target_funding_info_rate, funding_info.rate)

    @aioresponses()
    @patch("asyncio.Queue.get")
    def test_listen_for_funding_info_update_updates_funding_info(self, mock_api, mock_queue_get):
        url = self.funding_info_url

        response = self.funding_info_mock_response
        mock_api.get(url, body=json.dumps(response))

        url = web_utils.get_rest_url_for_endpoint(endpoint=CONSTANTS.GET_CONTRACT_INFO_PATH_URL.format(symbol=self.exchange_trading_pair))
        regex_url = re.compile(f"^{url}".replace(".", r"\.").replace("?", r"\?"))
        funding_resp = self.get_predicted_funding_info
        mock_api.get(regex_url, body=json.dumps(funding_resp))

        funding_info_event = self.funding_info_event_for_websocket_update()

        event_messages = [funding_info_event, asyncio.CancelledError]
        mock_queue_get.side_effect = event_messages

        try:
            self.async_run_with_timeout(
                self.exchange._listen_for_funding_info())
        except asyncio.CancelledError:
            pass

        self.assertEqual(1, self.exchange._perpetual_trading.funding_info_stream.qsize())  # rest in OB DS tests

    def _order_cancelation_request_successful_mock_response(self, order: InFlightOrder) -> Any:
        return {
            "code": "200000",
            "data": {
                "cancelledOrderIds": [
                    order.exchange_order_id
                ]
            }
        }

    def _order_status_request_completely_filled_mock_response(self, order: InFlightOrder) -> Any:
        return {
            "code": "200000",
            "data": {
                    "id": order.exchange_order_id or "2b1d811c-8ff0-4ef0-92ed-b4ed5fd6de34",
                    "symbol": self.exchange_trading_pair,
                    "type": "limit",
                    "side": order.trade_type.name.lower(),
                    "price": str(order.price),
                    "size": float(order.amount),
                    "value": float(order.price + 2),
                    "dealValue": float(order.price + 2),
                    "dealSize": float(order.amount),
                    "stp": "",
                    "stop": "",
                    "stopPriceType": "",
                    "stopTriggered": True,
                    "stopPrice": None,
                    "timeInForce": "GTC",
                    "postOnly": False,
                    "hidden": False,
                    "iceberg": False,
                    "leverage": "5",
                    "forceHold": False,
                    "closeOrder": False,
                    "visibleSize": "",
                    "clientOid": order.client_order_id or "",
                    "remark": None,
                    "tags": None,
                    "isActive": False,
                    "cancelExist": False,
                    "createdAt": 1558167872000,
                    "updatedAt": 1558167872000,
                    "endAt": 1558167872000,
                    "orderTime": 1558167872000000000,
                    "settleCurrency": order.quote_asset,
                    "status": "done",
                    "filledValue": float(order.price + 2),
                    "filledSize": float(order.amount),
                    "reduceOnly": False,
            }
        }

    def _order_status_request_canceled_mock_response(self, order: InFlightOrder) -> Any:
        resp = self._order_status_request_completely_filled_mock_response(order)
        resp["data"]["cancelExist"] = True
        resp["data"]["dealSize"] = 0
        resp["data"]["dealValue"] = 0
        return resp

    def _order_status_request_open_mock_response(self, order: InFlightOrder) -> Any:
        resp = self._order_status_request_completely_filled_mock_response(order)
        resp["data"]["status"] = "open"
        resp["data"]["dealSize"] = 0
        resp["data"]["dealValue"] = 0
        return resp

    def _order_status_request_partially_filled_mock_response(self, order: InFlightOrder) -> Any:
        resp = self._order_status_request_completely_filled_mock_response(order)
        resp["data"]["status"] = "open"
        resp["data"]["dealSize"] = float(self.expected_partial_fill_amount)
        resp["data"]["dealValue"] = float(self.expected_partial_fill_price)
        return resp

    def _order_fills_request_partial_fill_mock_response(self, order: InFlightOrder):
        return {
            "code": "200000",
            "data": {
                "currentPage": 1,
                "pageSize": 1,
                "totalNum": 251915,
                "totalPage": 251915,
                "items": [
                    {
                        "symbol": self.exchange_trading_pair,
                        "tradeId": self.expected_fill_trade_id,
                        "orderId": order.exchange_order_id,
                        "side": order.trade_type.name.lower(),
                        "liquidity": "taker",
                        "forceTaker": True,
                        "price": str(self.expected_partial_fill_price),  # Filled price
                        "size": float(self.expected_partial_fill_amount),  # Filled amount
                        "filledSize": float(self.expected_partial_fill_amount),  # Filled amount
                        "value": "0.00012227",  # Order value
                        "feeRate": "0.0005",  # Floating fees
                        "fixFee": "0.00000006",  # Fixed fees
                        "feeCurrency": "XBT",  # Charging currency
                        "stop": "",  # A mark to the stop order type
                        "fee": str(self.expected_fill_fee.percent),  # Transaction fee
                        "orderType": order.order_type.name.lower(),  # Order type
                        "tradeType": "trade",  # Trade type (trade, liquidation, ADL or settlement)
                        "createdAt": 1558334496000,  # Time the order created
                        "settleCurrency": order.base_asset,  # settlement currency
                        "tradeTime": 1558334496000000000  # trade time in nanosecond
                    }]
            }
        }

    def _order_fills_request_full_fill_mock_response(self, order: InFlightOrder):
        self._simulate_trading_rules_initialized()
        return {
            "code": "200000",
            "data": {
                    "currentPage": 1,
                    "pageSize": 100,
                    "totalNum": 1000,
                    "totalPage": 10,
                    "items": [
                        {
                            "symbol": self.exchange_trading_pair,  # Symbol of the contract
                            "tradeId": self.expected_fill_trade_id,  # Trade ID
                            "orderId": order.exchange_order_id,  # Order ID
                            "side": order.trade_type.name.lower(),  # Transaction side
                            "liquidity": "taker",  # Liquidity- taker or maker
                            "forceTaker": True,  # Whether to force processing as a taker
                            "price": str(order.price),  # Filled price
                            "matchPrice": str(order.price),  # Filled price
                            "size": float(self.exchange.get_quantity_of_contracts(self.trading_pair, order.amount)),   # Order amount
                            "filledSize": float(order.amount),   # Filled amount
                            "matchSize": float(order.amount),   # Filled amount
                            "value": "0.001204529",  # Order value
                            "feeRate": "0.0005",  # Floating fees
                            "fixFee": "0.00000006",  # Fixed fees
                            "feeCurrency": "USDT",  # Charging currency
                            "stop": "",  # A mark to the stop order type
                            "fee": str(self.expected_fill_fee.percent),  # Transaction fee
                            "orderType": order.order_type.name.lower(),  # Order type
                            "tradeType": "trade",  # Trade type (trade, liquidation, ADL or settlement)
                            "createdAt": 1558334496000,  # Time the order created
                            "settleCurrency": order.base_asset,  # settlement currency
                            "tradeTime": 1558334496000000000,  # trade time in nanosecond
                            "ts": 1558334496000000000  # trade time in nanosecond
                        }
                    ]
            }
        }

    def _simulate_trading_rules_initialized(self):
        self.exchange._trading_rules = {
            self.trading_pair: TradingRule(
                trading_pair=self.trading_pair,
                min_order_size=Decimal(str(0.01)),
                min_price_increment=Decimal(str(0.0001)),
                min_base_amount_increment=Decimal(str(0.000001)),
            ),
            self.non_linear_trading_pair: TradingRule(  # non-linear
                trading_pair=self.non_linear_trading_pair,
                min_order_size=Decimal(str(0.01)),
                min_price_increment=Decimal(str(0.0001)),
                min_base_amount_increment=Decimal(str(0.000001)),
            ),
        }

    @aioresponses()
    def test_update_order_status_when_order_has_not_changed_and_one_partial_fill(self, mock_api):
        # KuCoin has no partial fill status
        pass

    @aioresponses()
    def test_update_order_status_when_order_partially_filled_and_cancelled(self, mock_api):
        # KuCoin has no partial fill status
        pass

    @aioresponses()
    def test_user_stream_update_for_partially_cancelled_order(self, mock_api):
        # KuCoin has no partial fill status
        pass

    @aioresponses()
    def test_set_position_mode_success(self, mock_api):
        # There's only ONEWAY position mode
        pass

    @aioresponses()
    def test_set_position_mode_failure(self, mock_api):
        # There's only ONEWAY position mode
        pass

    def configure_order_not_found_error_cancelation_response(
            self, order: InFlightOrder, mock_api: aioresponses,
            callback: Optional[Callable] = lambda *args, **kwargs: None
    ) -> str:
        # Implement the expected not found response when enabling test_cancel_order_not_found_in_the_exchange
        raise NotImplementedError

    def configure_order_not_found_error_order_status_response(
            self, order: InFlightOrder, mock_api: aioresponses,
            callback: Optional[Callable] = lambda *args, **kwargs: None
    ) -> List[str]:
        # Implement the expected not found response when enabling
        # test_lost_order_removed_if_not_found_during_order_status_update
        raise NotImplementedError

    @aioresponses()
    def test_cancel_order_not_found_in_the_exchange(self, mock_api):
        # Disabling this test because the connector has not been updated yet to validate
        # order not found during cancellation (check _is_order_not_found_during_cancelation_error)
        pass

    @aioresponses()
    def test_lost_order_removed_if_not_found_during_order_status_update(self, mock_api):
        # Disabling this test because the connector has not been updated yet to validate
        # order not found during status update (check _is_order_not_found_during_status_update_error)
        pass

    @aioresponses()
    def test_create_buy_limit_maker_order_successfully(self, mock_api):
        self._simulate_trading_rules_initialized()
        request_sent_event = asyncio.Event()
        self.exchange._set_current_timestamp(1640780000)

        url = self.order_creation_url

        creation_response = self.order_creation_request_successful_mock_response

        mock_api.post(url,
                      body=json.dumps(creation_response),
                      callback=lambda *args, **kwargs: request_sent_event.set())

        order_id = self.place_buy_limit_maker_order()
        self.async_run_with_timeout(request_sent_event.wait())

        order_request = self._all_executed_requests(mock_api, url)[0]
        self.validate_auth_credentials_present(order_request)
        self.assertIn(order_id, self.exchange.in_flight_orders)
        request_data = json.loads(order_request.kwargs["data"])
        self.assertEqual(True, request_data["postOnly"])

    @aioresponses()
    @patch("hummingbot.connector.derivative.kucoin_perpetual.kucoin_perpetual_derivative.KucoinPerpetualDerivative.get_price")
    def test_create_buy_market_order_successfully(self, mock_api, get_price_mock):
        get_price_mock.return_value = Decimal(10000)
        self._simulate_trading_rules_initialized()
        request_sent_event = asyncio.Event()
        self.exchange._set_current_timestamp(1640780000)

        url = self.order_creation_url

        creation_response = self.order_creation_request_successful_mock_response

        mock_api.post(url,
                      body=json.dumps(creation_response),
                      callback=lambda *args, **kwargs: request_sent_event.set())

        order_id = self.place_buy_market_order()
        self.async_run_with_timeout(request_sent_event.wait())

        order_request = self._all_executed_requests(mock_api, url)[0]
        self.validate_auth_credentials_present(order_request)
        self.assertIn(order_id, self.exchange.in_flight_orders)
        request_data = json.loads(order_request.kwargs["data"])
        self.assertEqual("IOC", request_data["timeInForce"])

    @aioresponses()
    def test_update_order_status_processes_trade_fill(self, mock_api):
        self.exchange._set_current_timestamp(1640780000)
        self._simulate_trading_rules_initialized()
        request_sent_event = asyncio.Event()

        self.exchange.start_tracking_order(
            order_id="OID1",
            exchange_order_id="EOID1",
            trading_pair=self.trading_pair,
            order_type=OrderType.LIMIT,
            trade_type=TradeType.BUY,
            price=Decimal("10000"),
            amount=Decimal("1"),
        )
        order: InFlightOrder = self.exchange.in_flight_orders["OID1"]

        self.configure_fill_history_trade_response(
            order=order,
            mock_api=mock_api,
            callback=lambda *args, **kwargs: request_sent_event.set())
        self.async_run_with_timeout(self.exchange._update_trade_history())

        self.async_run_with_timeout(request_sent_event.wait())
        fill_event = self.order_filled_logger.event_log[0]

        self.assertEqual(1, len(self.order_filled_logger.event_log))
        self.assertEqual(self.exchange.current_timestamp, fill_event.timestamp)
        self.assertEqual(order.client_order_id, fill_event.order_id)
        self.assertEqual(order.trading_pair, fill_event.trading_pair)
        self.assertEqual(order.trade_type, fill_event.trade_type)
        self.assertEqual(order.order_type, fill_event.order_type)
        self.assertEqual(order.price, fill_event.price)
        self.assertEqual(order.amount, fill_event.amount)
        expected_fee = self.expected_trade_history_fill_fee
        self.assertEqual(expected_fee, fill_event.trade_fee)

    @aioresponses()
    def test_start_network_update_trading_rules(self, mock_api):
        self.exchange._set_current_timestamp(1000)

        url = self.trading_rules_url

        response = self.trading_rules_request_mock_response
        results = response
        duplicate = deepcopy(results['data'][0])
        duplicate["symbol"] = f"{self.exchange_trading_pair}_12345"
        duplicate["multiplier"] = str(float(duplicate["multiplier"]) + 1)
        results['data'].append(duplicate)
        mock_api.get(url, body=json.dumps(response))

        self.async_run_with_timeout(self.exchange.start_network())

        self.assertEqual(1, len(self.exchange.trading_rules))
        self.assertIn(self.trading_pair, self.exchange.trading_rules)
        self.assertEqual(repr(self.expected_trading_rule), repr(self.exchange.trading_rules[self.trading_pair]))

    @aioresponses()
    def test_user_stream_update_for_order_full_fill(self, mock_api):
        self.exchange._set_current_timestamp(1640780000)
        self._simulate_trading_rules_initialized()
        leverage = 2
        self.exchange._perpetual_trading.set_leverage(self.trading_pair, leverage)
        self.exchange.start_tracking_order(
            order_id=self.client_order_id_prefix + "1",
            exchange_order_id=self.exchange_order_id_prefix + "1",
            trading_pair=self.trading_pair,
            order_type=OrderType.LIMIT,
            trade_type=TradeType.SELL,
            price=Decimal("10000"),
            amount=Decimal("1"),
            position_action=PositionAction.OPEN,
        )
        order = self.exchange.in_flight_orders[self.client_order_id_prefix + "1"]

        order_event = self.order_event_for_full_fill_websocket_update(order=order)
        trade_event = self.trade_event_for_full_fill_websocket_update(order=order)
        expected_unrealized_pnl = 12
        position_event = self.position_event_for_full_fill_websocket_update(
            order=order, unrealized_pnl=expected_unrealized_pnl
        )

        mock_queue = AsyncMock()
        event_messages = []
        if trade_event:
            event_messages.append(trade_event)
        if order_event:
            event_messages.append(order_event)
        if position_event:
            event_messages.append(position_event)
        event_messages.append(asyncio.CancelledError)
        mock_queue.get.side_effect = event_messages
        self.exchange._user_stream_tracker._user_stream = mock_queue

        if self.is_order_fill_http_update_executed_during_websocket_order_event_processing:
            self.configure_full_fill_trade_response(
                order=order,
                mock_api=mock_api)

        try:
            self.async_run_with_timeout(self.exchange._user_stream_event_listener())
        except asyncio.CancelledError:
            pass
        # Execute one more synchronization to ensure the async task that processes the update is finished
        self.async_run_with_timeout(order.wait_until_completely_filled())

        fill_event = self.order_filled_logger.event_log[0]
        self.assertEqual(self.exchange.current_timestamp, fill_event.timestamp)
        self.assertEqual(order.client_order_id, fill_event.order_id)
        self.assertEqual(order.trading_pair, fill_event.trading_pair)
        self.assertEqual(order.trade_type, fill_event.trade_type)
        self.assertEqual(order.order_type, fill_event.order_type)
        self.assertEqual(order.price, fill_event.price)
        self.assertEqual(order.amount, fill_event.amount)
        expected_fee = self.expected_fill_fee
        self.assertEqual(expected_fee, fill_event.trade_fee)
        self.assertEqual(leverage, fill_event.leverage)
        self.assertEqual(PositionAction.OPEN.value, fill_event.position)

        sell_event = self.sell_order_completed_logger.event_log[0]
        self.assertEqual(self.exchange.current_timestamp, sell_event.timestamp)
        self.assertEqual(order.client_order_id, sell_event.order_id)
        self.assertEqual(order.base_asset, sell_event.base_asset)
        self.assertEqual(order.quote_asset, sell_event.quote_asset)
        self.assertEqual(order.amount, sell_event.base_asset_amount)
        self.assertEqual(order.amount * fill_event.price, sell_event.quote_asset_amount)
        self.assertEqual(order.order_type, sell_event.order_type)
        self.assertEqual(order.exchange_order_id, sell_event.exchange_order_id)
        self.assertNotIn(order.client_order_id, self.exchange.in_flight_orders)
        self.assertTrue(order.is_filled)
        self.assertTrue(order.is_done)

        self.assertTrue(
            self.is_logged(
                "INFO",
                f"SELL order {order.client_order_id} completely filled."
            )
        )

        self.assertEqual(1, len(self.exchange.account_positions))

        position: Position = self.exchange.account_positions[self.trading_pair]
        self.assertEqual(self.trading_pair, position.trading_pair)
        self.assertEqual(PositionSide.SHORT, position.position_side)
        self.assertEqual(expected_unrealized_pnl, position.unrealized_pnl)
        self.assertEqual(fill_event.price, position.entry_price)
        self.assertEqual(-fill_event.amount, (self.exchange.get_quantity_of_contracts(self.trading_pair, position.amount)))
        self.assertEqual(leverage, position.leverage)

    @aioresponses()
    def test_lost_order_user_stream_full_fill_events_are_processed(self, mock_api):
        self.exchange._set_current_timestamp(1640780000)
        self._simulate_trading_rules_initialized()
        self.exchange.start_tracking_order(
            order_id=self.client_order_id_prefix + "1",
            exchange_order_id=str(self.expected_exchange_order_id),
            trading_pair=self.trading_pair,
            order_type=OrderType.LIMIT,
            trade_type=TradeType.BUY,
            price=Decimal("10000"),
            amount=Decimal("1"),
        )
        order = self.exchange.in_flight_orders[self.client_order_id_prefix + "1"]

        for _ in range(self.exchange._order_tracker._lost_order_count_limit + 1):
            self.async_run_with_timeout(
                self.exchange._order_tracker.process_order_not_found(client_order_id=order.client_order_id))

        self.assertNotIn(order.client_order_id, self.exchange.in_flight_orders)

        order_event = self.order_event_for_full_fill_websocket_update(order=order)
        trade_event = self.trade_event_for_full_fill_websocket_update(order=order)

        mock_queue = AsyncMock()
        event_messages = []
        if trade_event:
            event_messages.append(trade_event)
        if order_event:
            event_messages.append(order_event)
        event_messages.append(asyncio.CancelledError)
        mock_queue.get.side_effect = event_messages
        self.exchange._user_stream_tracker._user_stream = mock_queue

        if self.is_order_fill_http_update_executed_during_websocket_order_event_processing:
            self.configure_full_fill_trade_response(
                order=order,
                mock_api=mock_api)

        try:
            self.async_run_with_timeout(self.exchange._user_stream_event_listener())
        except asyncio.CancelledError:
            pass
        # Execute one more synchronization to ensure the async task that processes the update is finished
        self.async_run_with_timeout(order.wait_until_completely_filled())

        fill_event = self.order_filled_logger.event_log[0]
        self.assertEqual(self.exchange.current_timestamp, fill_event.timestamp)
        self.assertEqual(order.client_order_id, fill_event.order_id)
        self.assertEqual(order.trading_pair, fill_event.trading_pair)
        self.assertEqual(order.trade_type, fill_event.trade_type)
        self.assertEqual(order.order_type, fill_event.order_type)
        self.assertEqual(order.price, fill_event.price)
        self.assertEqual(order.amount, fill_event.amount)
        expected_fee = self.expected_fill_fee
        self.assertEqual(expected_fee, fill_event.trade_fee)

        self.assertEqual(0, len(self.buy_order_completed_logger.event_log))
        self.assertNotIn(order.client_order_id, self.exchange.in_flight_orders)
        self.assertNotIn(order.client_order_id, self.exchange._order_tracker.lost_orders)
        self.assertTrue(order.is_filled)
        self.assertTrue(order.is_failure)


from django.db import models, transaction
from decimal import Decimal
from django.core.validators import MinValueValidator
from rest_framework import serializers
import logging

# Configuración del logger
logger = logging.getLogger(__name__)

class Customer(models.Model):
    first_name = models.CharField(max_length=50)
    last_name = models.CharField(max_length=50)
    email = models.EmailField(unique=True)
    date_of_birth = models.DateField()
    external_id = models.CharField(max_length=255, unique=True)
    score = models.PositiveIntegerField()

    ACTIVE = 1
    INACTIVE = 2
    STATUS_CHOICES = [
        (ACTIVE, 'Activo'),
        (INACTIVE, 'Inactivo'),
    ]
    status = models.IntegerField(choices=STATUS_CHOICES, default=ACTIVE)

    @property
    def total_balance(self):
        accepted_status = Loan.ACCEPTED
        total_loaned = sum(loan.amount for loan in self.loans.all() if loan.status == accepted_status)
        total_paid = sum(payment.total_amount for loan in self.loans.all() for payment in loan.payments.filter(status=Payment.ACCEPTED))
        balance = total_loaned - total_paid
        return balance

    @property
    def total_debt(self):
        # Filtrando los préstamos del cliente con estado PENDING o ACCEPTED
        relevant_loans = self.loans.filter(status__in=[Loan.PENDING, Loan.ACCEPTED])
        # Sumando el 'outstanding' de todos estos préstamos para obtener la deuda total
        total_debt_value = sum(loan.outstanding for loan in relevant_loans)
        logger.info(f"Calculando deuda total para el cliente {self.id}. Préstamos relevantes: {[loan.id for loan in relevant_loans]}. Deuda total: {total_debt_value}")
        return total_debt_value

    @property
    def available_amount(self):
        return self.score - self.total_debt

    def __str__(self):
        return f'{self.first_name} {self.last_name}'

class Loan(models.Model):
    customer = models.ForeignKey(Customer, on_delete=models.CASCADE, related_name='loans')
    amount = models.DecimalField(max_digits=10, decimal_places=2)
    external_id = models.CharField(max_length=255, unique=True)
    contract_version = models.TextField(null=True, blank=True)

    PENDING = 1
    ACCEPTED = 2
    REJECTED = 3
    PAID = 4
    STATUS_CHOICES = [
        (PENDING, 'Pending'),
        (ACCEPTED, 'Accepted'),
        (REJECTED, 'Rejected'),
        (PAID, 'Paid'),
    ]

    status = models.IntegerField(choices=STATUS_CHOICES, default=PENDING)
    outstanding = models.DecimalField(max_digits=10, decimal_places=2)
    taken_at = models.DateTimeField(null=True, blank=True)

    @property
    def balance(self):
        total_accepted_payments = sum(payment.amount for payment in self.payments.filter(status=Payment.ACCEPTED))
        balance = self.amount - Decimal(total_accepted_payments)
        return balance

    def recalculate_balance(self):
        total_accepted_payments = sum(payment.total_amount for payment in self.payments.filter(status=Payment.ACCEPTED))
        self.outstanding = self.amount - Decimal(total_accepted_payments)
        if self.outstanding == 0:
            self.status = Loan.PAID
        else:
            self.status = Loan.ACCEPTED
        self.save()

    def mark_as_paid(self):
        if self.outstanding == Decimal('0'):
            self.status = Loan.PAID
            self.save()

    @transaction.atomic
    def apply_payment(self, payment_amount):
        """Apply a payment to the loan. Update the outstanding amount and possibly change the loan's status."""
        logger.info(f"Aplicando pago de {payment_amount} a préstamo con saldo pendiente inicial: {self.outstanding}")
        if payment_amount <= self.outstanding:
            self.outstanding -= payment_amount
            if self.outstanding == 0:
                self.status = Loan.PAID
            else:
                self.status = Loan.ACCEPTED
            self.save()
            logger.info(f"Pago aplicado. Nuevo saldo pendiente para el préstamo: {self.outstanding}")
            return True
        logger.warning(f"El pago de {payment_amount} excede el saldo pendiente de {self.outstanding}. Pago no aplicado.")
        return False

class Payment(models.Model):
    external_id = models.CharField(max_length=255, unique=True)
    customer = models.ForeignKey(Customer, on_delete=models.CASCADE)
    total_amount = models.DecimalField(max_digits=10, decimal_places=2, validators=[MinValueValidator(0.01)])
    loan = models.ForeignKey(Loan, on_delete=models.CASCADE, related_name='payments')

    PROCESSING = 1
    ACCEPTED = 2
    REJECTED = 3
    STATUS_CHOICES = [
        (PROCESSING, 'Processing'),
        (ACCEPTED, 'Accepted'),
        (REJECTED, 'Rejected'),
    ]

    status = models.IntegerField(choices=STATUS_CHOICES, default=PROCESSING)
    created_at = models.DateTimeField(auto_now_add=True)

class PaymentDetail(models.Model):
    payment = models.ForeignKey(Payment, on_delete=models.CASCADE)
    loan = models.ForeignKey(Loan, on_delete=models.CASCADE)
    amount = models.DecimalField(max_digits=10, decimal_places=2)

    @classmethod
    def get_loans_by_customer(cls, customer):
        return cls.objects.filter(customer=customer)


import json
import time
from tqdm import tqdm

def extract_recommand(data_path,suggest_path,result_path):
    #read suggestion file
    f1=open(suggest_path,encoding='utf-8')
    suggest=f1.readlines()
    #extract suggestions
    feature=['ByAppointmentOnly','BusinessParking','GoodForKids','BikeParking','distance','DogsAllowed','price', 'staff', 'crowd', 'drink', 'trainer', 'contract', 'membership', 'class', 'card', 'kid', 'clean', 'wait', 'sign', 'parking', 'equipment', 'service', 'room', 'woman', 'music', 'atmosphere', 'yoga', 'open', 'pool', 'bar', 'AC', 'locker', 'management', 'contact', 'ball', 'hot', 'space', 'cardio', 'massage', 'counter', 'refund', '24/7', 'treadmill', 'bathroom', 'floor']
    #construct suggestions for each feature
    fea_dict={}
    for line in suggest:
        if line.split('\t')[0] in feature:
            if line.split('\t')[0]=='distance':
                fea_dict[line.split('\t')[0]]=[line.split('\t')[1].strip('\n'),line.split('\t')[2].strip('\n')]
            else:
                fea_dict[line.split('\t')[0]]=line.split('\t')[1].strip('\n"')
    print(fea_dict)
    t=[]
    sug_dict={}
    f2=open(result_path,'w')
    for line in tqdm(open(data_path,encoding='utf-8')):
        #load data
        data=dict(json.loads(line))
        #delete and transfer some features
        data1={}
        data1['business_id']=data.pop('business_id')
        data1['name']=data.pop('name')
        data1['stars']=data.pop('stars')
        data1['city']=data.pop('city')
        data1['state']=data.pop('state')
        data1['postal_code']=data.pop('postal_code')
        data1['suggested_postal_code']=data.pop('suggested_postal_code')
        a={}
        #extract nontext features
        a['ByAppointmentOnly']=data.pop('ByAppointmentOnly')
        a['BusinessParking']=data.pop('BusinessParking')
        a['GoodForKids']=-1*data.pop('GoodForKids')
        a['BikeParking']=data.pop('BikeParking')
        a['DogsAllowed']=data.pop('DogsAllowed')
        #delete useless text feature
        day=['weekend_close','weekday_close','weekday_open','weekend_open','BusinessAcceptsCreditCards','time','week','day','year','month','issue','stuff','environment']
        for days in day:
            data.pop(days)
        #extract the first three text features with lowest sentiment score
        text=[]
        for i in range(0,3):
            key=min(data,key=lambda k:data[k])
            data.pop(key)
            text.append(key)
        for texts in text:
            if texts not in t:
                t.append(texts)
        data1['text']=text
        #extract the first three nontext features that the business does not have
        count=0
        nontext=[]
        for keys in a:
            if a[keys]==-1:
                nontext.append(keys)
                count+=1
                if count==3:
                    break
        data1['nontext']=nontext
        #construct suggestions
        recommand=''
        #suggestions for text features
        for texts in text:
            print(texts)
            #for distance, if there exist recommanded postal code, use it as the suggestion
            if texts=='distance':
                if data1['suggested_postal_code']!=None:
                    recommand=recommand+fea_dict[texts][0]+str(data1['suggested_postal_code'])+'.\t'
                else:
                    recommand=recommand+fea_dict[texts][1]+'\t'
            else:
                recommand=recommand+fea_dict[texts]+'\t'
        #suggestions for nontext features
        for nontexts in nontext:
            print(nontexts)
            if nontexts=='BusinessParking':
                if 'parking' not in text:
                    recommand=recommand+fea_dict[nontexts]+'\t'
            elif nontexts=='GoodForKids':
                if 'kid' not in text:
                    recommand=recommand+fea_dict[nontexts]+'\t'
            else:
                recommand=recommand+fea_dict[nontexts]+'\t'
        data1['suggest']=recommand
        print(data1)
        #put all suggestions in a dictionary
        sug_dict[data1['business_id']]=recommand.strip('\t')
    #write suggestions to file
    f2.write(json.dumps(sug_dict))
    print(t)
    print(len(t))

def get_result(data_path,sug_path,result_path):
    #load suggestions generated in extract_recommand
    with open(sug_path,encoding='utf-8') as f1:
        suggestions=dict(json.loads(f1.readline()))
    f2=open(result_path,'w')
    #write file head
    f2.write('name|city|state|address|suggestion\n')
    for lines in tqdm(open(data_path,encoding='utf-8')):
        #load data
        data=dict(json.loads(lines))
        if data['business_id'] in suggestions:
            try:
                #write gym info and suggestions to result file
                gym=data['name']+'|'+data['city']+'|'+data['state']+'|'+data['address']+'|'+suggestions[data['business_id']]+'\n'
                f2.write(gym)
            except:
                #if there is some code which are unrecognized under utf-8, deal with it case by case.
                if data['name'].encode('utf_8')==b'\xc3\x89nergie Cardio Complexe Desjardins':
                    gym='Energie Cardio Complexe Desjardins|Montreal|QC|175 Boul Rene-Levesque O'+suggestions[data['business_id']]+'\n'
                    print(gym)
                    f2.write(gym)
                if data['name'].encode('utf_8')==b'M\xc3\x9cV Integrated Physical Culture':
                    gym='MUV Integrated Physical Culture|Pittsburgh|PA|5469 Penn Ave'+suggestions[data['business_id']]+'\n'
                    print(gym)
                    f2.write(gym)
                if data['name'].encode('utf_8')==b'Centre sportif de Notre-Dame-de-Gr\xc3\xa2ce':
                    gym='Centre sportif de Notre-Dame-de-Grace|Montreal|QC|6445 Avenue de Monkland'+suggestions[data['business_id']]+'\n'
                    print(gym)
                    f2.write(gym)

if __name__ == '__main__':
    extract_recommand('./Data/advice.json','./Data/feature_importance.txt','./Data  /suggestions.json')
    get_result('./Data/business_gym.json','./Data/suggestions.json','./Data/gym_suggestion.csv')


from typing import List, Tuple
from collections import OrderedDict
from copy import deepcopy

def load_data() -> Tuple[OrderedDict, List[int], List[List[int]]]:
    """
    :return:
    requirements_dict: E.g. {"departure track": [50, 150, 156, 950], ...}
        4 values represent the 4 boundaries in order of increased size
    my_ticket (List[int])
    other_tickets: list of tickets (list of lists of ints)
    """
    requirements_dict = OrderedDict()
    my_ticket = []
    other_tickets = []
    phase = 1
    for line in open('data/day_16.txt'):
        line = line.replace('\n', '')
        if not line:
            continue
        if 'ticket' in line:
            phase += 1
            continue
        if phase == 1:  # load requirements
            requirement, requirement_value = line.split(': ')
            r12, r34 = requirement_value.split(' or ')
            r1, r2 = [int(x) for x in r12.split('-')]
            r3, r4 = [int(x) for x in r34.split('-')]
            requirements_dict[requirement] = [r1, r2, r3, r4]
        if phase == 2:  # my ticket
            my_ticket = [int(x) for x in line.split(',')]
        if phase == 3:
            other_tickets.append([int(x) for x in line.split(',')])
    return requirements_dict, my_ticket, other_tickets

def task_1(requirements_dict: OrderedDict, other_tickets: List[List[int]]) -> int:
    accumulator = 0
    for ticket in other_tickets:
        for value in ticket:
            for r1, r2, r3, r4 in requirements_dict.values():
                if r1 <= value <= r2 or r3 <= value <= r4:
                    break  # number is valid
            else:  # number doesn't meet any requirements
                accumulator += value
    return accumulator

def task_2(requirements_dict: OrderedDict, my_ticket: List[int], other_tickets: List[List[int]]) -> int:

    # find invalid tickets
    invalid_tickets = []
    for i, ticket in enumerate(other_tickets):
        for value in ticket:
            for r1, r2, r3, r4 in requirements_dict.values():
                if r1 <= value <= r2 or r3 <= value <= r4:
                    break  # number is valid
            else:  # number doesn't meet any requirements
                invalid_tickets.append(i)

    # find potential positions for each requirement
    potential_positions = {}
    for requirement in requirements_dict:
        potential_positions[requirement] = list(range(len(my_ticket)))
    for j, ticket in enumerate(other_tickets):
        if j in invalid_tickets:
            continue
        for requirement, ranges in requirements_dict.items():
            r1, r2, r3, r4 = ranges
            for i, value in enumerate(ticket):
                valid = r1 <= value <= r2 or r3 <= value <= r4
                if not valid:
                    if i in potential_positions[requirement]:
                        potential_positions[requirement].remove(i)

    # organise potential positions
    actual_positions = {}  # requirement(str): position(int)
    while potential_positions:
        for requirement in deepcopy(potential_positions):
            for position in actual_positions.values():
                if position in potential_positions[requirement]:
                    potential_positions[requirement].remove(position)
            assert potential_positions[requirement]
            if len(potential_positions[requirement]) == 1:
                actual_positions[requirement] = potential_positions.pop(requirement)[0]

    # finally, add the departure values and return
    accumulator = 1
    for requirement, position in actual_positions.items():
        if "departure" in requirement:
            index = actual_positions[requirement]
            accumulator *= my_ticket[index]
    return accumulator

def main():
    requirements_dict, my_ticket, other_tickets = load_data()
    print(task_1(requirements_dict, other_tickets))
    print(task_2(requirements_dict, my_ticket, other_tickets))

if __name__ == '__main__':
    main()


#coding:utf-8
import sys
import os
sys.path.insert(0,'..')
import numpy as np
from os.path import join
import pickle
import cv2
from tqdm import tqdm

from core.model import P_Net, R_Net
from Detection.detector import Detector
from Detection.fcn_detector import FcnDetector
from Detection.MtcnnDetector import MtcnnDetector
from config import cfg

try:
    from .loader import TestLoader
    from .utils import IoU, convert_to_square
except ImportError:
    from loader import TestLoader
    from utils import IoU, convert_to_square

def SaveTxt(file, target, idx, content, types):
    assert types in ['part', 'neg', 'pos']
    name = join('%s_%s'%(target, types), '%s.jpg'%idx)
    file.write(name + ' ' + content)

def SaveImg(img, target, idx, types):
    folder = join(cfg.path_output_files, '%s_%s'%(target, types))
    path = join(folder, '%s.jpg'%idx)
    cv2.imwrite(path, img)

def save_hard_example(target, data, save_path):
    # load ground truth from annotation file
    # format of each line: image/path [x1,y1,x2,y2] for each gt_box in this image
    image_size = cfg.resize[target]
    im_idx_list = data['images']
    gt_boxes_list = data['bboxes']
    num_of_images = len(im_idx_list)

    neg_file = open(join(cfg.path_output_txt, '%s_neg.txt'%target), 'w')
    pos_file = open(join(cfg.path_output_txt, '%s_pos.txt'%target), 'w')
    part_file = open(join(cfg.path_output_txt, '%s_part.txt'%target), 'w')

    dirs = ['neg', 'part', 'pos']
    dirs = [join(cfg.path_output_files, '%s_%s'%(target, d)) for d in dirs]
    for d in dirs:
        if not os.path.exists(d): os.makedirs(d)

    det_boxes = pickle.load(open(save_path, 'rb'))

    assert len(det_boxes) == num_of_images, "incorrect detections or ground truths"

    # index of neg, pos and part face, used as their image names
    n_idx = 0
    p_idx = 0
    d_idx = 0
    # image_done = 0
    #im_idx_list image index(list)
    #det_boxes detect result(list)
    #gt_boxes_list gt(list)
    for im_idx, dets, gts in tqdm(zip(im_idx_list, det_boxes, gt_boxes_list), total=num_of_images):
        gts = np.array(gts, dtype=np.float32).reshape(-1, 4)

        if dets.shape[0] == 0:
            continue
        img = cv2.imread(im_idx)
        #change to square
        dets = convert_to_square(dets)
        dets[:, 0:4] = np.round(dets[:, 0:4])
        neg_num = 0
        for box in dets:
            x_left, y_top, x_right, y_bottom, _ = box.astype(int)
            width = x_right - x_left + 1
            height = y_bottom - y_top + 1

            # ignore box that is too small or beyond image border
            if width < 20 or x_left < 0 or y_top < 0 or x_right > img.shape[1] - 1 or y_bottom > img.shape[0] - 1:
                continue

            # compute intersection over union(IoU) between current box and all gt boxes
            Iou = IoU(box, gts)
            cropped_im = img[y_top:y_bottom + 1, x_left:x_right + 1, :]
            resized_im = cv2.resize(cropped_im, (image_size, image_size),
                                    interpolation=cv2.INTER_LINEAR)

            # save negative images and write label
            # Iou with all gts must below 0.3

            if np.max(Iou) < 0.3 and neg_num < 60:
                content = '0\n'
                SaveTxt(neg_file, target, n_idx, content, 'neg')
                SaveImg(resized_im, target, n_idx, 'neg')

                n_idx += 1
                neg_num += 1
            else:
                # find gt_box with the highest iou
                idx = np.argmax(Iou)
                assigned_gt = gts[idx]
                x1, y1, x2, y2 = assigned_gt

                # compute bbox reg label
                offset_x1 = (x1 - x_left) / float(width)
                offset_y1 = (y1 - y_top) / float(height)
                offset_x2 = (x2 - x_right) / float(width)
                offset_y2 = (y2 - y_bottom) / float(height)

                # save positive and part-face images and write labels
                if np.max(Iou) >= 0.65:
                    content = '1 %.2f %.2f %.2f %.2f\n' % (offset_x1, offset_y1, offset_x2, offset_y2)
                    SaveTxt(pos_file, target, p_idx, content, 'pos')
                    SaveImg(resized_im, target, p_idx, 'pos')
                    p_idx += 1

                elif np.max(Iou) >= 0.4:
                    content = '-1 %.2f %.2f %.2f %.2f\n' % (offset_x1, offset_y1, offset_x2, offset_y2)
                    SaveTxt(part_file, target, d_idx, content, 'part')
                    SaveImg(resized_im, target, d_idx, 'part')
                    d_idx += 1
    neg_file.close()
    part_file.close()
    pos_file.close()

def GetModelPaths():
    path_pnet = join(cfg.path_output_models, 'pnet', 'pnet-%s'%cfg.epoch[0])
    path_rnet = join(cfg.path_output_models, 'rnet', 'rnet-%s'%cfg.epoch[1])
    path_onet = join(cfg.path_output_models, 'onet', 'onet-%s'%cfg.epoch[2])
    return [path_pnet, path_rnet, path_onet]

def read_annotation():
    annotation = dict()
    paths = []
    bboxes = []
    with open(cfg.path_detection_labels, 'r') as f:
        file = f.readlines()

    for line in file:
        line = line.strip().split(sep=' ')
        path = line[0]
        path = join(cfg.path_detection_imgs, path)
        data = line[1:]
        assert len(data)%4==0
        data = np.array(data, dtype=np.float32).reshape(-1, 4)
        data = list(data)
        data = [list(box) for box in data]
        paths.append(path)
        bboxes.append(data)

    annotation['images'] = paths
    annotation['bboxes'] = bboxes
    return annotation

def Run(target):
    assert target in ['rnet', 'onet']

    model_path = GetModelPaths()
    batch_size = [2048, 256, 16]
    thresh = [0.4, 0.05, 0.7]
    min_face_size = 24

    detectors = [None, None, None]
    detectors[0] = FcnDetector(P_Net, model_path[0])

    if target == 'onet':
        detectors[1] = Detector(R_Net, 24, batch_size[1], model_path[1])

    data = read_annotation()

    if cfg.debug:
        data['bboxes'] = data['bboxes'][:20]
        data['images'] = data['images'][:20]

    test_data = TestLoader(data['images'])

    mtcnn_detector = MtcnnDetector(detectors=detectors, min_face_size=min_face_size, stride=2, threshold=thresh)

    detections, _ = mtcnn_detector.detect_face(test_data)

    save_file = join(cfg.path_output_files, 'detections_%s.pkl'%target)
    with open(save_file, 'wb') as f:
        pickle.dump(detections, f, 1)
    save_hard_example(target, data, save_file)

def GenHardExampleRnet():
    Run(target='rnet')
def GenHardExampleOnet():
    Run(target='onet')

if __name__=='__main__':
    pass
    #GenHardExampleRnet()
    #GenHardExampleOnet()


import json
import os
import logging
import pytest

logger = logging.getLogger(__name__)

@pytest.mark.skip(reason="Can't test this due to persmission issues")
def test_post(nc):
    with open('./test/integration/data/vendor_credit/data.json') as oj:
        s = oj.read()
        vendor_credit = json.loads(s)
    logger.debug('vendor_credit = %s', vendor_credit)
    res = nc.vendor_credits.post(vendor_credit)
    logger.debug('res = %s', res)
    assert res['externalId'] == vendor_credit['externalId'], 'Transaction Number does not match'
    assert res['type'] == 'vendorCredit', 'Type does not match'


'''
Pass your auth0 client ID and secret to authenticate and retrieve your token
'''

import requests

class AxilAuthorizer:

    def __init__(self, client_id, client_secret, client_token = None):
        self.client_id = client_id
        self.client_secret = client_secret
        self.token = client_token

        if client_token is None:
            self.authenticate()

    def authenticate(self):
        payload = {
            'client_id': self.client_id,
            'client_secret': self.client_secret,
            'audience': 'https://api.axil.ai',
            'grant_type': 'client_credentials'
        }

        headers = {'content-type': 'application/json'}

        req_resp = requests.post('https://axil.auth0.com/oauth/token',
                         headers=headers,
                         json=payload,
                         )
        if req_resp.status_code != requests.codes.ok:
            raise Exception(f'Error occurred getting Bearer token: {req_resp.reason}')

        self.token = req_resp.json()['access_token']


from django.conf import settings
from django.conf.urls.static import static
from django.conf.urls import url, include
from django.contrib import admin
from .views import home_page,about_page,contact_page
from accounts.views import login_page,RegisterView,logout_page,ActivateAccount
from django.views.generic import TemplateView

urlpatterns = [
    url(r'^login/$',login_page, name='login'),
    url(r'^logout/$',logout_page,name='logout'),
    url(r'^register/$',RegisterView.as_view(),name='register'),
    url(r'^activate-account/(?P<pk>\d+)/$', ActivateAccount.as_view(), name='activate-account'),
    # url(r'^$',home_page, name='home'),
    url(r'^about/$', about_page, name='about'),
    url(r'^contact/$', contact_page,name='contact'),
    url(r'^admin/', admin.site.urls),
    url(r'^bootstrap/', TemplateView.as_view(template_name='bootstrap/example.html')),
    url(r'^', include("books.urls", namespace="books")),
    url(r'^student-register/',include("register.urls", namespace="student-register")),
    # url(r'^cart/', include("carts.urls", namespace="cart")),
    url(r'^search/', include("search.urls", namespace="search")),
    url(r'^profile/', include("profile.urls", namespace="profile")),

]

if settings.DEBUG:
    urlpatterns = urlpatterns + static(settings.STATIC_URL, document_root=settings.STATIC_ROOT)
    urlpatterns = urlpatterns + static(settings.MEDIA_URL, document_root=settings.MEDIA_ROOT)

#url(r'^products/$', ProductListView.as_view()),
    #url(r'^products-fbv/$', product_list_view),
    #url(r'^products/(?P<pk>\d+)/$',ProductDetailView.as_view()),
    #url(r'^products/(?P<slug>[\w-]+)/$', ProductDetailSlugView.as_view()),
    #url(r'^products-fbv/(?P<pk>\d+)/$',product_detail_view),
    #url(r'^featured/$', ProductFeaturedListView.as_view()),
    #url(r'^featured/(?P<pk>\d+)/$', ProductFeaturedDetailView.as_view()),


import pandas as pd
import numpy as np

unrate = pd.read_csv(r"D:\untitled\UNRATE.csv")
unrate['DATE'] = pd.to_datetime(unrate['DATE']) #时间格式转换
#print(unrate)

import matplotlib.pyplot as plt
# plt.plot()  #画
# plt.show()  # 展示

# 存入数据
first_unrate = unrate[0:12] #取前12行的数据
plt.plot(first_unrate['DATE'],first_unrate['VALUE'])
plt.xticks(rotation = 45)
plt.xlabel('MONTH') #添加标题
plt.ylabel('VALUE')
plt.title('Monthly Unemployment Trends, 1948')
plt.show()