Incorrect response from the API

@Hardik during reconciliation, at EOD, the positions API returned incorrect data for one of the positions. Afterwards it corrected.

This is too big of an issue! If the API starts behaving inconsistently, how can I trust the system?

Hello @sv28

Can you share the actual response received on API and value stored in your DB. We checked the logs at our end and the data sent has price as 1162.0 and quantity as 39 only.

Do confirm with the actual response received.

@hardik This was the state in my db:

  "BSE:ADANIENSOL-A": {
    "buy_order_placed": true,
    "status": "TRADED",
    "units": 39,
    "buy_price": 1162,
    "trailing_profit_activated": false,
    "time": "2024-01-04 10:32:06",
    "strategy_id": "nova_tp20_t55_nifty"
  }

I don’t store the API response, however, here is the code which called the API

import json

from Core.constants import KEY_STATE
from Core.utils import Utils
from main import logger

MARGIN_OF_ERROR = 0.2


def reconcile_holdings():
    """
    Reconcile holdings with the broker's records.
    """
    db, _, dhan = Utils.get_common_resources()
    holdings_from_broker = dhan.get_holdings()['data']
    positions = dhan.get_positions()['data']

    holdings_from_broker = filter_holdings(holdings_from_broker)
    positions = filter_positions(positions)

    common_symbols = get_common_symbols(holdings_from_broker, positions)

    update_common_holdings(common_symbols, holdings_from_broker, positions)

    holdings_from_broker.extend(positions)
    holdings_from_db = json.loads(db.kv_get(KEY_STATE))

    check_holdings(holdings_from_db, holdings_from_broker)

    db.kv_save(KEY_STATE, json.dumps(holdings_from_db))


def filter_holdings(holdings):
    return [h for h in holdings if h['availableQty'] > 0]


def filter_positions(positions):
    return [p for p in positions if p['netQty'] > 0 and p['positionType'] == 'LONG']


def get_common_symbols(holdings, positions):
    symbols_from_broker = [h['tradingSymbol'] for h in holdings]
    symbols_from_positions = [p['tradingSymbol'] for p in positions]
    return list(set(symbols_from_broker) & set(symbols_from_positions))


def update_common_holdings(common_symbols, holdings, positions):
    for symbol in common_symbols:
        holding = next((h for h in holdings if symbol.__contains__(h['tradingSymbol'])), None)
        position = next((p for p in positions if symbol.__contains__(p['tradingSymbol'])), None)
        logger.error(f"There is common holding for {symbol}. Holding: {holding}, Position: {position}. Check this and "
                     f"update the code.")
        # holdings['tradingSymbol']['availableQty'] += position['netQty']


def check_holdings(holdings_from_db, holdings_from_broker):
    if len(holdings_from_db) != len(holdings_from_broker):
        logger.error(f"Number of holdings mismatch. DB: {len(holdings_from_db)}, "
                     f"Broker: {len(holdings_from_broker)}")

    for symbol, db_holding in holdings_from_db.items():
        broker_holding = next((h for h in holdings_from_broker if symbol.__contains__(h['tradingSymbol'])), None)
        db_holding_price = round(db_holding['buy_price'], 2)
        broker_holding_price = round(broker_holding.get('avgCostPrice', broker_holding.get('buyAvg')), 2)

        if broker_holding is None:
            logger.error(f"No broker record found for {symbol}")
            continue

        check_quantity(symbol, db_holding, broker_holding)
        check_price(symbol, db_holding_price, broker_holding_price, holdings_from_db)


def check_quantity(symbol, db_holding, broker_holding):
    if db_holding['units'] != broker_holding.get('availableQty', broker_holding.get('netQty')):
        logger.error(f"Quantity mismatch for {symbol}. DB: {db_holding['units']}, "
                     f"Broker: {broker_holding.get('availableQty', broker_holding.get('netQty'))}")


def check_price(symbol, db_holding_price, broker_holding_price, holdings_from_db):
    if db_holding_price != broker_holding_price:
        difference = (db_holding_price - broker_holding_price) * 100 / broker_holding_price
        if (db_holding_price > broker_holding_price) and difference <= MARGIN_OF_ERROR:
            logger.info(f"DB price is higher than broker price for {symbol}. "
                        f"DB: {db_holding_price}, Broker: {broker_holding_price}")
            holdings_from_db[symbol]['buy_price'] = broker_holding_price
        else:
            logger.error(f"DB price is higher than broker price for {symbol}. "
                         f"DB: {db_holding_price}, Broker: {broker_holding_price}. Difference %: {difference}")

if __name__ == '__main__':
    reconcile_holdings()

Also, as I previously said:

The positions API returned incorrect data for one of the positions. Afterwards it corrected.

Hey @sv28

We checked all logs for that day, wherein Positions API were called, and all showed same response for the mentioned instrument.

Let us know if this issue occurs again, but we couldn’t find logs for this issue here.