# 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

## TODO:
## Flowchart
