---
# System prepended metadata

title: Flexchain Instruction Document

---

# Flexchain Instruction Document

## API Links:

##### (POST, GET): 
> FlexibilityOfferFunction: https://flexchain-functionapp-testenv.azurewebsites.net/api/flexibilityOffers
> FlexibilityRequestFunction: https://flexchain-functionapp-testenv.azurewebsites.net/api/flexibilityRequests
> Flex_MatchingAlgo_Function: https://flexchain-functionapp-testenv.azurewebsites.net/api/flex_matching_algo_Results



##### (GET) (a specific entry)
> FlexibilityOfferGetFunction: https://flexchain-functionapp-testenv.azurewebsites.net/api/flexibilityOffers/{UserID}
> FlexibilityRequestGetFunction: https://flexchain-functionapp-testenv.azurewebsites.net/api/flexibilityRequests/{RequestID}
> Flex_MatchingAlgo_Get_Function: https://flexchain-functionapp-testenv.azurewebsites.net/api/flex_matching_algo_Results/{RequestID}



##### (DELETE: specific entry) [by DSO: ALL USING {RequestID}]
> FlexibilityOfferDelFunction: https://flexchain-functionapp-testenv.azurewebsites.net/api/flexibilityOffers/{RequestID}
> FlexibilityRequestDelFunction: https://flexchain-functionapp-testenv.azurewebsites.net/api/flexibilityRequests/{RequestID}
> Flex_MatchingAlgoDelFunction: https://flexchain-functionapp-testenv.azurewebsites.net/api/flex_matching_algo_Results/{RequestID}


##### (Blind MatchingAlgo GET):
> Flex_MatchingAlgo_Blind_Function: https://flexchain-functionapp-testenv.azurewebsites.net/api/flex_matching_algo_Results_blind/{RequestID}


##### Validity Check Function (GET):
> ValidityCheck_Get_Function: https://flexchain-functionapp-testenv.azurewebsites.net/api/flex_validity_check/
> 

#### Timer Trigger Functions

DeleteFlexOfferTimerTrigger: timerTrigger

DeleteFlexRequestTimerTrigger: timerTrigger

DeletematchingAlgoTimerTrigger: timerTrigger

MatchingAlgoPostTimerTrigger: timerTrigger


## Offer, Request, Matching Algo Body Format (JSON):

### Offer Format:

```
"UserId": "user001",
            "FlexOfferList": [
                {
                    "BidPriceCtpEUList": "null",
                    "endFlexShiftTimeSlot": "2021-10-05T21:00:00Z",
                    "RequestId": "Req000001",
                    "startFlexShiftTimeSlot": "2021-10-05T19:00:00Z",
                    "totalFlexOfferedEU": -6,
                    "id": null
                },
                {
                    "BidPriceCtpEUList": "null",
                    "endFlexShiftTimeSlot": "2021-10-05T19:15:00Z",
                    "RequestId": "Req000002",
                    "startFlexShiftTimeSlot": "2021-10-05T19:00:00Z",
                    "totalFlexOfferedEU": 2,
                    "id": null
                },
                {
                    "BidPriceCtpEUList": "8, 10, 12, 13",
                    "endFlexShiftTimeSlot": "2021-10-05T19:15:00Z",
                    "RequestId": "Req000006",
                    "startFlexShiftTimeSlot": "2021-10-05T19:00:00Z",
                    "totalFlexOfferedEU": 4,
                    "id": null
                },
                {
                    "BidPriceCtpEUList": "7",
                    "endFlexShiftTimeSlot": "2021-10-05T19:15:00Z",
                    "RequestId": "Req000007",
                    "startFlexShiftTimeSlot": "2021-10-05T19:00:00Z",
                    "totalFlexOfferedEU": 4,
                    "id": null
                }
            ]
```



### Request Format:

#### (for fixed price):

```
"RequestId": "Req000001",
            "Mode": "fcfs",
            "FullfillmentFactor": 50,
            "MarketType": "fixedPrice",
            "MaxPriceCtpEU": null,
            "PriceOfferCtpEU": "7",
            "ReferencePriceCtpEU": "null",
            "TimeSlot": "2021-10-05T15:15:00Z",
            "TotalFlexRequestedEU": "-11",
            “MatchingAlgoCheck”: false



 "RequestId": "Req000005",
            "Mode": "fcfs",
            "FullfillmentFactor": 0,
            "MarketType": "fixedPrice",
            "MaxPriceCtpEU": null,
            "PriceOfferCtpEU": "null",
            "ReferencePriceCtpEU": "null",
            "TimeSlot": "2021-06-13T22:16:00Z",
            "TotalFlexRequestedEU": "null",
            “MatchingAlgoCheck”: false
```


#### (for auction):

```
"RequestId": "Req000006",
            "Mode": "MiP",
            "FullfillmentFactor": "50",            
            "MarketType": "auction",
            "MaxPriceCtpEU": 12,
            "PriceOfferCtpEU": "null",
            "ReferencePriceCtpEU": "10",
            "TimeSlot": "2021-10-05T15:00:00Z",
            "TotalFlexRequestedEU": "7",
            “MatchingAlgoCheck”: false



 "RequestId": "Req000008",
            "Mode": "MiP",
            "FullfillmentFactor": null,
            "MarketType": "auction",
            "MaxPriceCtpEU": null,
            "PriceOfferCtpEU": "null",
            "ReferencePriceCtpEU": "null",
            "TimeSlot": "2021-10-05T15:00:00Z",
            "TotalFlexRequestedEU": "null",
            “MatchingAlgoCheck”: false
```


### Matching Algo Result Format (The format in which matching algo results are stored in the table):

```
"response": [
        {
          "_id": {
            "$oid": "640aea889d4f350d11121249"
          },
          "requestId": "req0001",
          "reachedFullFillmentFactor": true,
          "results": [
            {
              "userId": "us1",
              "flexEU": 6
            },
            {
              "userId": "us2",
              "flexEU": 4
            },
            {
              "userId": "us3",
              "flexEU": 1
            }
          ]
        },
        {
          "_id": {
            "$oid": "640aea889d4f350d1112124c"
          },
          "requestId": "req00011",
          "reachedFullFillmentFactor": false,
          "results": null
        }
    ]

```


## Note:
### In Requests:
**‘market_type’ can be:**  

‘fixed_price’: The grid operator offers a fixed price (“reference_price_ctpEU”: null, “price_offered_ctpEU”: [int]) for flexibility 

‘auction’: A market based auction decides the price (“reference_price_ctpEU”: [int], “price_offered_ctpEU”: null) 

<!-- ‘isBlind’ can be:  

True: The volume requested by the grid operator is not known (“total_flex_requested_EU”: null) 

False: The volume requested by the grid operator is known (“total_flex_requested_EU”: [int])  -->


**‘loc’:** List of HEMS UserIds with suitable location

### In Offers:
In case **‘market_type’: ‘fixed_price’,** the price is already written in the flexibility request. Therefore: 
‘price_bid_ctEU_list’ list:null. 

In case **‘market_type’: ‘auction’,** the EU prices are sent as list 
‘price_bid_ctpEU_list’: […] 

**"start_flex_shift_timeslot ":** start of timeslot to which flex must be shifted, null if "traded_flexibility_EU":0 

**"end_flex_shift_timeslot ":** start of timeslot to which flex must be shifted, null if "traded_flexibility_EU":0 

## Details on the Modes:

Types of modes:
#### fcfs, maah, maav, (miah), miav, mip, zufall


Suitability to market type (auction or fixed price):
*  **auction market type:** fcfs, mip, maah, (miah),  maav, miav, zufall
* **fixed price market type:** fcfs, maah, maav, (miah), miav, zufall




### FCFS: First come, first served
#### market typ: fixed price

```
If flexibilityOffer.UserId is user 
                    	{                      
                        For each uservalue in flexibilityOffer.FlexOfferList
                        {
                            If uservalue.RequestID is flexibilityRequest.RequestID
                            {
                                If SIGN>0 && uservalue.TotalFlexOfferedEU > 0) or (SIGN<0 && uservalue.TotalFlexOfferedEU < 0 
                                {
                                    If SIGN<0 && uservalue.TotalFlexOfferedEU < 0
                                    {
                                        uservalue.TotalFlexOfferedEU = Math.Abs(uservalue.TotalFlexOfferedEU)
                                    }
                                    If TOTALFLEXREQUESTED - uservalue.TotalFlexOfferedEU > 0
                                    {
                                        TOTALFLEXREQUESTED-= uservalue.TotalFlexOfferedEU
                                        accepted_offers[flexibilityOffer.UserId] = uservalue.TotalFlexOfferedEU
                                    }
                                    else
                                    {
                                        accepted_offers[flexibilityOffer.UserId] = TOTALFLEXREQUESTED
                                        return accepted_offers;
                                    }
                                }
                            }
                        }
                    }
```


### MAAH: Maximale Anzahl HEMS
#### market type: fixed price, auction
```

For each uservalue in flexibilityOffer.FlexOfferList
                        {
                            if uservalue.RequestID is flexibilityRequest.RequestID
                            {
                                if ((SIGN > 0 && uservalue.TotalFlexOfferedEU > 0) or (SIGN < 0 && uservalue.TotalFlexOfferedEU < 0))
                                {
                                    if SIGN < 0 && uservalue.TotalFlexOfferedEU < 0)
                                    {
                                        uservalue.TotalFlexOfferedEU = Math.Abs(uservalue.TotalFlexOfferedEU)

                                    }

                                    POTENTIALOFFER[flexibilityOffer.UserId] = uservalue.TotalFlexOfferedEU
                                }
                            }
                        }

SUM=0
For each  i in POTENTIALOFFER.Keys
            {
                SUM += POTENTIALOFFER[i];
            }
            if SUM <= TOTALFLEXREQUESTED
            {
                return POTENTIALOFFER
            }
            else
            {
                var ordered = POTENTIALOFFER.OrderBy(x.Value).todictionary();
                
                for each i in ordered.Keys
                {
                    if TOTALFLEXREQUESTED > ordered[i]
                    {
                        accepted_offers[i] = ordered[i]
                        TOTALFLEXREQUESTED -= ordered[i]
                    }
                    else
                    {
                        accepted_offers[i] = TOTALFLEXREQUESTED
                        return accepted_offers
                    }
                }
                return accepted_offers;
            }
        }
```

### MAAV: Maximale Anzahl Verträge
#### market typ: fixed price, auction
```

if uservalue.RequestID is flexibilityRequest.RequestID
                            {
                                if ((SIGN > 0 && uservalue.TotalFlexOfferedEU > 0) or (SIGN < 0 && uservalue.TotalFlexOfferedEU < 0))
                                {
                                    if (SIGN < 0 && uservalue.TotalFlexOfferedEU < 0)
                                    {
                                        uservalue.TotalFlexOfferedEU = Math.Abs(uservalue.TotalFlexOfferedEU)
                                    }
                                    var usertotalflexrequested = uservalue.TotalFlexOfferedEU
                                    while (usertotalflexrequested not 0)
                                    {
                                        POTENTIALOFFER.Add(flexibilityOffer.UserId)
                                        usertotalflexrequested--
                                    }

                                }
                            }

POTENTIALOFFER.Shuffle();
            if POTENTIALOFFER.Count() > TOTALFLEXREQUESTED
            {
                while(TOTALFLEXREQUESTED not 0)
                {
                    if(accepted_offers.ContainsKey(POTENTIALOFFER[0]))
                    {
                        accepted_offers[POTENTIALOFFER[0]] = accepted_offers[POTENTIALOFFER[0]] + 1
                    }
                    else
                    {
                        accepted_offers[POTENTIALOFFER[0]] = 1
                    }
                    TOTALFLEXREQUESTED--
                    POTENTIALOFFER.RemoveAt(0)
                }
            }
            else
            {
                For each offer in POTENTIALOFFER
                {
                    If accepted_offers.ContainsKey(offer)
                    {
                        accepted_offers[offer] = accepted_offers[offer] + 1
                    }
                    else
                    {
                        accepted_offers[offer] = 1
                    }
                }
            }
            return accepted_offers;
        }
```


### MIAV: Minimale Anzahl Verträge
#### market typ: fixed price, auction
```

if uservalue.RequestID is flexibilityRequest.RequestID
                            {
                                if ((SIGN > 0 && uservalue.TotalFlexOfferedEU > 0) or (SIGN < 0 && uservalue.TotalFlexOfferedEU < 0))
                                {
                                    if SIGN < 0 && uservalue.TotalFlexOfferedEU < 0
                                    {
                                        uservalue.TotalFlexOfferedEU = Math.Abs(uservalue.TotalFlexOfferedEU)
                                    }
                                    POTENTIALOFFER[flexibilityOffer.UserId] = uservalue.TotalFlexOfferedEU
                                }
                            }

For each i in POTENTIALOFFER.Keys
            {
                SUM += POTENTIALOFFER[i]
            }
            if (SUM <= TOTALFLEXREQUESTED)
            {
                return POTENTIALOFFER
            }
            else
            {
                var ordered = POTENTIALOFFER.OrderByDescending(x => x.Value).todictionary()

                for each i in ordered.Keys
                {
                    if (TOTALFLEXREQUESTED > ordered[i])
                    {
                        accepted_offers[i] = ordered[i]
                        TOTALFLEXREQUESTED -= ordered[i]
                    }
                    else
                    {
                        accepted_offers[i] = TOTALFLEXREQUESTED
                        return accepted_offers
                    }
                }
                return accepted_offers
            }
```

### MIAH: Minimale Anzahl HEMS

> the logic for this trading mode is identical with “Minimale Anzahl Verträge (MiAV)”


### MiP: Minimaler Preis
#### market typ: auction

```
if (uservalue.RequestID is flexibilityRequest.RequestID)
                            {
                                if ((SIGN > 0 && uservalue.TotalFlexOfferedEU > 0) or (SIGN < 0 && uservalue.TotalFlexOfferedEU < 0))
                                {
                                    if (SIGN < 0 && uservalue.TotalFlexOfferedEU < 0)
                                    {
                                        uservalue.TotalFlexOfferedEU = Math.Abs(uservalue.TotalFlexOfferedEU);

                                    }
                                    BIDS[user] = uservalue.BidPriceCtpEUList;
                                    
                                }
                            }

For each username in BIDS.Keys
            {
                var COUNT = 0
                var userbids = BIDS[username]
                for each userbid in userbids
                {
                    if userbid <= flexibilityRequest.MaxPriceCtpEU
                    {
                        POTENTIALOFFER[ (COUNT).ToString() + " " + username] = userbid

                        COUNT+=1;
                    }
                }

            }

            var sortedDict = POTENTIALOFFER.OrderBy(x => x.Value).todictionary()
            for each key in sortedDict.Keys
            {
                if(TOTALFLEXREQUESTED>1)
                {
                    TOTALFLEXREQUESTED--;
                    if (accepted_offers.ContainsKey(key))
                    {
                        accepted_offers[key] += POTENTIALOFFER[key];
                    }
                    else
                    {
                        accepted_offers[key] = POTENTIALOFFER[key];
                    }
                }
                else
                {
                    if (accepted_offers.ContainsKey(key))
                    {
                        accepted_offers[key] += POTENTIALOFFER[key];
                    }
                    else
                    {
                        accepted_offers[key] = POTENTIALOFFER[key];
                    }

                    return accepted_offers
                }
                
            }
            return accepted_offers
```

### Zufall:

> trading mode is identical with “Maximal Anzahl Verträge (MaAV)”


At one time all the request modes should be same (all **fcfs**, or all **mip** or all **maav** …).

#### Note:
For https://flexchain-functionapp-testenv.azurewebsites.net/api/flex_matching_algo_Results

> We do not put any details (Json) as the body for POST request. **The server will take data from FlexibilityRequest and FlexibilityOffer and use the Matching Algorithm.** The result of the matching algorithm is stored in the the flex_matching_algo_Results table in the database.


## Cron Jobs

### DeleteALL Cron Job: 
#### Every 2 weeks.
```
await requestcontroller.DropCollection();
             
await offerController.DropCollection();

await matchingController.DropCollection();
```


### MatchingAlgo POST Cron Job:
#### Every 15 min.
```
await matchingController.Post(null);
```

## (Blind MatchingAlgo GET):
To call 
`http://localhost:7071/api/flex_matching_algo_Results_blind/{RequestID}`

As **GET** method.

Body:
> {
> “userId” : “value”,
> “password” : “value”
> }


## Removing Duplicacy in Matching Algo Results
Since the Requests and Offers are only being removed once every 2 weeks, there can be a scenario where `A,B,C` requests and `W,X,Y,Z` offers give `P,Q,R` matching algorithm results at time `T`. Since the inputs are not removed, at time `T + 15min` another `P,Q,R` results will be created, thus making redundency.

To eliminate this, we have included a check called `MatchingAlgoCheck`. It is there for each request and it is set to false initially.

When matching algorithm is called, the `MatchingAlgoCheck` for all the used requests are set to true. And during matching algorithm calculation we are using an if statement that allows only requests with `MatchingAlgoCheck=false` to be included in the calculation.


## Blind Matching Algo
It is required when a user tries to check if their offer was accepted for a particular request (and if accepted, then how much) without gaining knowledge of the accepted offers from other users.

User has to use the endpoint with requestid:

https://flexchain-functionapp-testenv.azurewebsites.net/api/flex_matching_algo_Results_blind/{RequestID}

The body format for the GET request is:

{
```
"UserId": "us1",
"Password": "abc"
```
}

The response format is:

In case user offer was accepted:

```
{
    "status": 200,
    "errors": [],
    "warnings": [],
    "information": [],
    "response": {
        "us1": 6,
        "Total": 11
    }
}
```

In case user offer was not accepted:

```
{
    "status": 403,
    "errors": [
        {
            "message": "Offer Not Accepted",
            "type": "ForbiddenError",
            "data": null
        }
    ],
    "warnings": [],
    "information": [],
    "response": null
}
```

## Hash Format
```
RequestID + 

BidPriceCtpEUList + 

TotalFlexOfferedEU + 

StartFlexShiftTimeslot + 

EndFlexShiftTimeslot
```

So hash value: 
`SHA3(RequestID + BidPriceCtpEUList + TotalFlexOfferedEU + StartFlexShiftTimeslot + EndFlexShiftTimeslot)`



#### Merkle Tree
![](https://i.imgur.com/pkGha1O.png)


## TODO:



## Flowchart

![](https://i.imgur.com/eQMlt6Q.png)


