Kotak-Neo / kotak-neo-api

120 stars 112 forks source link

Unable to calculate PNL using formula provided in API docs #84

Open ShivdasBachewar opened 1 year ago

ShivdasBachewar commented 1 year ago

I have tried to calculate PNL for my positions. using formula provided in docs works fine for intraday positions. but its giving wrong calculation for Positional FNO Trades.

PnL = (Total Sell Amt - Total Buy Amt) + (Net qty * LTP * multiplier * (genNum/genDen) * (prcNum/prcDen) ) using formula provided from docs: https://github.com/Kotak-Neo/kotak-neo-api/blob/main/docs/Positions.md

@Kotak-Neo please provide any workaround.

ShivdasBachewar commented 1 year ago

for example, following is one entry from my positions.

row = {'buyAmt': '4250.00', 'cfSellAmt': '7287.50', 'prod': 'NRML', 'exSeg': 'nse_fo', 'sqrFlg': 'Y', 'actId': 'YL1ZK', 'cfBuyQty': '0', 'hsUpTm': '2023/10/30 12:36:26', 'cfSellQty': '250', 'tok': '78833', 'flBuyQty': '250', 'flSellQty': '0', 'sellAmt': '0.00', 'posFlg': 'true', 'cfBuyAmt': '0.00', 'stkPrc': '5900.00', 'trdSym': 'DRREDDY23NOV5900CE', 'sym': 'DRREDDY', 'multiplier': '1', 'precision': '2', 'expDt': '30 Nov, 2023', 'type': 'OPTSTK', 'genNum': '1', 'series': 'XX', 'prcNum': '1', 'genDen': '1', 'brdLtQty': 125, 'exp': '1701354600', 'lotSz': '125', 'optTp': 'CE', 'prcDen': '1'}

TotalSellAmt = float(row['cfSellAmt']) + float(row['sellAmt'])
TotalBuyAmt = float(row['cfBuyAmt']) + float(row['buyAmt'])
TotalBuyQty = float(row['cfBuyQty']) + float(row['flBuyQty'])
TotalSellQty = float(row['cfSellQty']) + float(row['flSellQty'])
CarryFwdQty = float(row['cfBuyQty']) - float(row['cfSellQty'])
NetQty = row['quantity'] = TotalBuyQty - TotalSellQty
LTP = 10.0# fetching LTP from websocket
pnl =  (TotalSellAmt - TotalBuyAmt) + (NetQty * LTP * float(row['multiplier']) * (float(row['genNum'])/float(row['genDen'])) * (float(row['prcNum'])/float(row['prcDen'])))
  1. it shows pnl = 3037.5
  2. but pnl in website is = 2895.73
  3. please guide how to resolve this

@Kotak-Neo, @shashwat001 @nbharadvajdatamatics pls help

ShivdasBachewar commented 1 year ago

@Kotak-Neo sir, please help me.

rosevinod commented 1 year ago

i am using this method:

def calculatePnL(live_position):
      global config_dict
      df = pd.DataFrame(live_position)
      for row in df.itertuples():
          # pprint(f"{row.data}")
          prow = pd.Series(row.data)
          # print(f"{prow.trdSym}")

          """
          PnL = github.com/Kotak-Neo/kotak-neo-api/blob/main/docs/Positions.md
          """
          total_buy_amt = float(prow.cfBuyAmt) + float(prow.buyAmt)
          total_sell_amt = float(prow.cfSellAmt) + float(prow.sellAmt)

          total_buy_qty = float(prow.cfBuyQty) + float(prow.flBuyQty)
          total_sell_qty = float(prow.cfSellQty) + float(prow.flSellQty)

          net_qty = total_buy_qty - total_sell_qty

          if net_qty > 0 or net_qty < 0:
              ltp = float(getLTPfromDB())
              # print(f"{total_sell_amt=}, {total_buy_amt=},{net_qty=},{ltp=}")

              pnl = (total_sell_amt - total_buy_amt) + (
                  net_qty
                  * ltp
                  * float(prow.multiplier)
                  * (float(prow.genNum) / float(prow.genDen))
                  * (float(prow.prcNum) / float(prow.prcDen))
              )

              pnl = round(pnl, 2)

              # pnl = (total_sell_amt - total_buy_amt) + (net_qty * ltp )
              # pnl = round(pnl,2)
              # print(f"short method: {pnl=}")

              # check the profit greater than 1% of opening price
              print(f"pnl: {pnl=}, {Config.PROFIT_AMT.value=}")
ShivdasBachewar commented 1 year ago

thanks @rosevinod, but my problem is with Positional trades MTM. intraday mtm calculation is working fine

rosevinod commented 1 year ago

I think you need to fetch position API, get avgprc of your position using websocket API ltp and avgprc of each second will provide the MTM of position.

Kotak-Neo commented 1 year ago

For carryforward positions, you may use the avg price from Portfolio API to calculate the PnL.

ShivdasBachewar commented 1 year ago

@Kotak-Neo pls provide a proper example

aswinm227 commented 1 year ago

you may calcuulate the pnl on t day itself and store it in an sql db

crypt0inf0 commented 11 months ago

Try,

def calculate_pnl(positions):
    # Extract data from the response
    data = positions.get("data", [])

    if not data:
        # print("No data found in the response")
        return

    pnl_results = []

    for trade in data:
        # Fetch the script symbol & ltp
        trdSym = trade.get("trdSym")
        ltp = float(18.05) # fetch ltp from api

        # Extract relevant fields
        cfBuyQty = int(trade.get("cfBuyQty"))
        flBuyQty = int(trade.get("flBuyQty"))
        cfSellQty = int(trade.get("cfSellQty"))
        flSellQty = int(trade.get("flSellQty"))
        cfBuyAmt = float(trade.get("cfBuyAmt"))
        buyAmt = float(trade.get("buyAmt"))
        cfSellAmt = float(trade.get("cfSellAmt"))
        sellAmt = float(trade.get("sellAmt"))

        multiplier = int(trade.get("multiplier"))
        genNum = int(trade.get("genNum"))
        genDen = int(trade.get("genDen"))
        prcNum = int(trade.get("prcNum"))
        prcDen = int(trade.get("prcDen"))

        # Calculate Total Buy Qty and Total Sell Qty
        total_buy_qty = cfBuyQty + flBuyQty
        total_sell_qty = cfSellQty + flSellQty
        net_qty = total_buy_qty - total_sell_qty

        # Calculate Total Buy Amt and Total Sell Amt
        total_buy_amt = cfBuyAmt + buyAmt
        total_sell_amt = cfSellAmt + sellAmt

        # Calculate PnL
        pnl = (total_sell_amt - total_buy_amt) + (net_qty * ltp * multiplier * (genNum/genDen) * (prcNum/prcDen))

        # Calculate Buy Avg Price if total_buy_qty is not zero
        if total_buy_qty != 0:
            buy_avg_price = (total_buy_qty > 0, total_buy_amt / (total_buy_qty * multiplier * (genNum/genDen) * (prcNum/prcDen)), 0)
        else:
            buy_avg_price = 0

        # Calculate Sell Avg Price if total_sell_qty is not zero
        if total_sell_qty != 0:
            sell_avg_price = (total_sell_qty > 0, total_sell_amt / (total_sell_qty * multiplier * (genNum/genDen) * (prcNum/prcDen)), 0)
        else:
            sell_avg_price = 0

        pnl_results.append({"trdSym": trdSym, "pnl": pnl, "buy_avg_price": buy_avg_price, "sell_avg_price": sell_avg_price})

    return pnl_results

p = {"stat": "Ok", "stCode": 200, "data": [{'buyAmt': '4250.00', 'cfSellAmt': '0', 'prod': 'NRML', 'exSeg': 'nse_fo', 'sqrFlg': 'Y', 'actId': 'YL1ZK', 'cfBuyQty': '0', 'hsUpTm': '2023/10/30 12:36:26', 'cfSellQty': '0', 'tok': '78833', 'flBuyQty': '250', 'flSellQty': '0', 'sellAmt': '0.00', 'posFlg': 'true', 'cfBuyAmt': '0.00', 'stkPrc': '5900.00', 'trdSym': 'DRREDDY23NOV5900CE', 'sym': 'DRREDDY', 'multiplier': '1', 'precision': '2', 'expDt': '30 Nov, 2023', 'type': 'OPTSTK', 'genNum': '1', 'series': 'XX', 'prcNum': '1', 'genDen': '1', 'brdLtQty': 125, 'exp': '1701354600', 'lotSz': '125', 'optTp': 'CE', 'prcDen': '1'},
                                           {"buyAmt": "2625.00", "cfSellAmt": "0.00", "prod": "NRML", "exSeg": "nse_fo", "sqrFlg": "Y", "actId": "PRS2206", "cfBuyQty": "0", "cfSellQty": "0", "tok": "53179", "flBuyQty": "25", "flSellQty": "25", "sellAmt": "2625.00", "posFlg": "true", "cfBuyAmt": "0.00", "stkPrc": "0.00", "trdSym": "BANKNIFTY21JULFUT", "sym": "BANKNIFTY", "expDt": "29 Jul, 2021", "type": "FUTIDX", "series": "XX", "brdLtQty": "25", "exp": "1627569000", "optTp": "XX", "genNum": "1", "genDen": "1", "prcNum": "1", "prcDen": "1", "lotSz": "25", "multiplier": "1", "precision": "2", "hsUpTm": "2021/07/13 18:34:44"}]}

pnl_result = calculate_pnl(p)
print(pnl_result)

returns [{'trdSym': 'DRREDDY23NOV5900CE', 'pnl': 262.5, 'buy_avg_price': 17.0, 'sell_avg_price': 0.0}, {'trdSym': 'BANKNIFTY21JULFUT', 'pnl': 0.0, 'buy_avg_price': 105.0, 'sell_avg_price': 105.0}]

crypt0inf0 commented 11 months ago

@Kotak-Neo Is it possible to include "nOrdNo": "220287600096097" in the client.positions() response? it would be helpful.

crypt0inf0 commented 11 months ago

@Kotak-Neo It is impossible to calculate the carry forward position without a database, Because the cfBuyAmt keeps changing for example, Today I buy this position for 25785 which is the cfBuyAmt value today, the market close at 300 points down so the 'cfBuyAmt' value will be 21285 and buyAmt will become zero on tomorrow open.

How can one calculate the PNL when there is no correct data available?

is it possible to transfer the value of buyAmt to cfBuyAmt if the position is not closed in intraday?

cfBuyAmt += buyAmt lets assume I buy 20K CE today at premium 100 and tomorrow cfBuyAmt = 100 and then once again I buy at premium 150 after market close cfBuyAmt = 250

Total PnL: 1982.25
{'tid': 'server5_2115112',
 'stat': 'Ok',
 'stCode': 200,
 'data': [{'buyAmt': '0.00',
   'cfSellAmt': '0.00',
   'prod': 'NRML',
   'exSeg': 'nse_fo',
   'sqrFlg': 'Y',
   'actId': '',
   'cfBuyQty': '15',
   'hsUpTm': '2023/12/26 10:59:29',
   'cfSellQty': '0',
   'tok': '72266',
   'flBuyQty': '0',
   'flSellQty': '0',
   'sellAmt': '0.00',
   'posFlg': 'true',
   'cfBuyAmt': '20223.75',
   'stkPrc': '48000.00',
   'trdSym': 'BANKNIFTY24FEB48000CE',
   'sym': 'BANKNIFTY',
   'multiplier': '1',
   'precision': '2',
   'expDt': '29 Feb, 2024',
   'type': 'OPTIDX',
   'genNum': '1',
   'series': 'XX',
   'prcNum': '1',
   'genDen': '1',
   'brdLtQty': 15,
   'exp': '1709217000',
   'lotSz': '15',
   'optTp': 'CE',
   'prcDen': '1'}]}
ShivdasBachewar commented 11 months ago

@crypt0inf0 bhai,

@Kotak-Neo don't even care to reply us. their api is not appropriate even docs is lagging. API is not stable. they do not have proper support.

those who of us trying to stick with them and rely on them will get patience tested. Check i have logged this query by 30-oct till date no proper resolution.

crypt0inf0 commented 10 months ago

@ShivdasBachewar using the client.holdings() it returns the correct averagePrice for carry forward positions.