diff --git a/proto/frequenz/api/electricity_trading/electricity_trading.proto b/proto/frequenz/api/electricity_trading/electricity_trading.proto deleted file mode 100644 index 4a64b75..0000000 --- a/proto/frequenz/api/electricity_trading/electricity_trading.proto +++ /dev/null @@ -1,23 +0,0 @@ -// Frequenz Electricity Trading API -// -// Specification for Electricity Trading API. -// -// TODO(cookiecutter): Add a more descriptive package description. -// -// Copyright: -// Copyright 2023 Frequenz Energy-as-a-Service GmbH -// -// License: -// MIT - -syntax = "proto3"; - -package frequenz.api.electricity_trading.electricity_trading; - -// An example message. -// -// TODO(cookiecutter): Remove this message. -message HelloWorld { - // The greeting. - string greet = 1; -} diff --git a/proto/frequenz/api/electricity_trading/v1/electricity_trading.proto b/proto/frequenz/api/electricity_trading/v1/electricity_trading.proto new file mode 100644 index 0000000..abd3901 --- /dev/null +++ b/proto/frequenz/api/electricity_trading/v1/electricity_trading.proto @@ -0,0 +1,590 @@ +// Frequenz Electricity Trading API +// +// Specification for Electricity Trading API. +// +// Copyright: +// Copyright 2023 Frequenz Energy-as-a-Service GmbH +// +// License: +// MIT + +syntax = "proto3"; + +package frequenz.api.electricity_trading.electricity_trading.v1; + +import "google/protobuf/struct.proto"; +import "google/protobuf/timestamp.proto"; +import "google/protobuf/field_mask.proto"; + +import "frequenz/api/common/v1/grid.proto"; +import "frequenz/api/common/v1/markets.proto"; +import "frequenz/api/common/v1/pagination.proto"; + +// Service providing operations related to order management. +service ElectricityTradingService { + // Creates a new order for a given Gridpool. + rpc CreateGridpoolOrder(CreateGridpoolOrderRequest) returns (CreateGridpoolOrderResponse); + + // Updates an existing order for a given Gridpool. + rpc UpdateGridpoolOrder(UpdateGridpoolOrderRequest) returns (UpdateGridpoolOrderResponse); + + // Cancels an existing order for a given Gridpool. + rpc CancelGridpoolOrder(CancelGridpoolOrderRequest) returns (CancelGridpoolOrderResponse); + + // Cancels all open orders for a given Gridpool. + rpc CancelAllGridpoolOrders(CancelAllOpenGridpoolOrdersRequest) returns (CancelAllGridpoolOrdersResponse); + + // Fetches the details of a specific order for a given Gridpool. + rpc GetGridpoolOrder(GetGridpoolOrderRequest) returns (GetGridpoolOrderResponse); + + // Lists all the orders for a given Gridpool. + rpc ListGridpoolOrders(ListGridpoolOrdersRequest) returns (ListGridpoolOrdersResponse); + + // Stream order updates for a given Gridpool. + rpc ReceiveGridpoolOrdersStream(ReceiveGridpoolOrdersStreamRequest) + returns (stream ReceiveGridpoolOrdersStreamResponse); + + // Lists all public executed historic orders (trades). + rpc ListPublicOrders(ListPublicOrdersRequest) returns (ListPublicOrdersResponse); + + // Stream all executed public orders (trades). + rpc ReceivePublicOrdersStream(ReceivePublicOrdersStreamRequest) + returns (stream ReceivePublicOrdersStreamResponse); +} + +// OrderExecutionOption defines specific behavior for the execution of an order. +// These options provide control on how an order is handled in the market. +enum OrderExecutionOption { + // Order remains open until it's fully fulfilled, cancelled by the client, + // `valid_until` timestamp is reached, or the end of the trading session. + ORDER_EXECUTION_OPTION_NONE = 0; + + // All or None: Order must be executed in its entirety, or not executed at all. + ORDER_EXECUTION_OPTION_AON = 1; + + // Fill or Kill: Order must be executed immediately in its entirety, or not at all. + ORDER_EXECUTION_OPTION_FOK = 2; + + // Immediate or Cancel: Any portion of an order that cannot be filled immediately will be cancelled. + ORDER_EXECUTION_OPTION_IOC = 3; +} + +// Enum for the order types that can be specified for an order. +enum OrderType { + // UNSPECIFIED: The order type has not been set. + ORDER_TYPE_UNSPECIFIED = 0; + + // LIMIT: Order to buy or sell at a specific price or better. + // It remains active until it is filled, cancelled, or expired. + ORDER_TYPE_LIMIT = 1; + + // STOP_LIMIT: An order that will be executed at a specified price, or better, + // after a given stop price has been reached. + ORDER_TYPE_STOP_LIMIT = 2; + + // ICEBERG: A large order divided into smaller lots to hide the actual order quantity. + // Only the visible part of the order is shown in the order book. + ORDER_TYPE_ICEBERG = 3; + + // BLOCK: (Not yet supported) User defined block order, generally a large quantity + // order filled all at once. + ORDER_TYPE_BLOCK = 4; + + // BALANCE: (Not yet supported) Balance order aims to balance supply and demand, + // usually at a specific location or within a system. + ORDER_TYPE_BALANCE = 5; + + // PRE: (Not yet supported) On exchange prearranged trade, a trade that has been + // privately negotiated and then submitted to the exchange. + ORDER_TYPE_PREARRANGED = 6; + + // PRIVATE: (Not yet supported) Private and confidential trade, not visible in the + // public order book and has no market impact. + ORDER_TYPE_PRIVATE = 7; +} + +// Enum for the side of the market that the order is on. +enum MarketSide { + // UNSPECIFIED: The side of the market has not been set. + MARKET_SIDE_UNSPECIFIED = 0; + + // BUY: Order to purchase electricity. This is referred to as a 'bid' in the order book. + MARKET_SIDE_BUY = 1; + + // SELL: Order to sell electricity. This is referred to as an 'ask' or 'offer' in the order book. + MARKET_SIDE_SELL = 2; +} + +// Enum for the state of an order. +enum OrderState { + // UNKNOWN: The order state is not known. This is usually the default state of a newly + // created order object before any operations have been applied. + ORDER_STATE_UNKNOWN = 0; + + // PENDING: The order has been sent to the marketplace but has not yet been confirmed. + // This can be due to awaiting validation or system processing. + ORDER_STATE_PENDING = 1; + + // ACTIVE: The order has been confirmed and is open in the market. + // It may be unfilled or partially filled. + ORDER_STATE_ACTIVE = 2; + + // FILLED: The order has been completely filled and there are no remaining quantities + // on the order. + ORDER_STATE_FILLED = 3; + + // CANCELED: The order has been cancelled. This can occur due to a cancellation request by + // the market participant, system, or market operator. + ORDER_STATE_CANCELED = 4; + + // CANCEL_REQUESTED: A cancellation request for the order has been submitted but the + // order is not yet removed from the order book. + ORDER_STATE_CANCEL_REQUESTED = 5; + + // CANCEL_REJECTED: The order cancellation request was rejected, likely due to it having + // already been filled or expired. + ORDER_STATE_CANCEL_REJECTED = 6; + + // EXPIRED: The order has not been filled within the defined duration and has expired. + ORDER_STATE_EXPIRED = 7; + + // FAILED: The order submission failed and was unable to be placed on the order book, + // usually due to a validation error or system issue. + ORDER_STATE_FAILED = 8; + + // HIBERNATE: The order has been entered into the system but is not currently exposed + // to the market. This could be due to certain conditions not yet being met. + ORDER_STATE_HIBERNATE = 9; + + // RECALL: The order has been recalled. This could be due to a system issue or a request + // from the market participant or market operator. + ORDER_STATE_RECALL = 10; +} + +// Represents an order in the electricity market. +message Order { + // The delivery area where the contract is to be delivered. The representation of the + // delivery area may vary by jurisdiction. + frequenz.api.common.v1.grid.DeliveryArea delivery_area = 2; + + // The delivery period for the contract, specified as a start and end timestamp in UTC. + // It represents the period during which the contract is expected to be fulfilled. + frequenz.api.common.v1.grid.DeliveryPeriod delivery_period = 3; + + // The type of order, such as LIMIT, STOP_LIMIT, ICEBERG etc. + // This determines how the order is to be executed in the market. + OrderType type = 4; + + // Indicates if the order is on the Buy or Sell side of the market. + MarketSide side = 5; + + // The limit price at which the contract is to be traded. This is the maximum price + // for a BUY order or the minimum price for a SELL order. + frequenz.api.common.v1.markets.Price price = 6; + + // The quantity of the contract being traded, specified in MWh. + frequenz.api.common.v1.markets.Energy quantity = 7; + + // Applicable for STOP_LIMIT orders. This is the stop price that triggers the limit order. + optional frequenz.api.common.v1.markets.Price stop_price = 8; + + // Applicable for ICEBERG orders. This is the price difference between the peak price + // and the limit price. + optional frequenz.api.common.v1.markets.Price peak_price_delta = 9; + + // Applicable for ICEBERG orders. This is the quantity of the order to be displayed + // in the order book. + optional frequenz.api.common.v1.markets.Energy display_quantity = 10; + + // Optional execution options such as All or None, Fill or Kill, etc. + optional OrderExecutionOption execution_option = 11; + + // Do not use if ExecutionOption is set to FOK or IOC. This is an optional UTC timestamp + // defining the time after which the order should be cancelled if not filled. + optional google.protobuf.Timestamp valid_until = 12; + + // Optional user-defined payload individual to a specific order. This can be any data + // that needs to be associated with the order. + // + // The field can store e.g. JSON objects containing details involved in the order. This feature can simplify + // application development by eliminating the need for complicated state management to remember the + // specifics of each order. + // + // By embedding this "state" within the order itself, you can include specifics like which microgrids consume + // or provide how much power. This makes it easier to manage complex orders and can simplify the logic + // required in applications. + // + // Example JSON payload: + // { + // "microgrids": [ + // { + // "microgrid_id": "1", + // "mwh": 1.0 + // }, + // { + // "microgrid_id": "2", + // "mwh": 0.5 + // } + // ] + // } + // + // In this example, if the order is exectuted, these microgrids might consume the electricity mentioned + // in the example JSON payload. + optional google.protobuf.Struct payload = 13; + + // Optional user-defined tag to group related orders. + optional string tag = 14; +} + +// Represents an order with full details, including its ID, state and associated UTC timestamps. +message OrderDetail { + // Inner message providing details about the current state of the order. + message StateDetail { + // Enum describing the action that led to the state change. + enum StateReason { + STATE_REASON_UNKNOWN = 0; + STATE_REASON_ADD = 1; // The order was added. + STATE_REASON_MODIFY = 2; // The order was modified. + STATE_REASON_DELETE = 3; // The order was deleted. + STATE_REASON_DEACTIVATE = 4; // The order was deactivated. + STATE_REASON_REJECT = 5; // The order was rejected. + STATE_REASON_FULL_EXECUTION = 6; // The order was fully executed. + STATE_REASON_PARTIAL_EXECUTION = 7; // The order was partially executed. + STATE_REASON_ICEBERG_SLICE_ADD = 8; // An iceberg slice was added. + STATE_REASON_VALIDATION_FAIL = 9; // The order failed validation. + STATE_REASON_UNKNOWN_STATE = 10; // The state of the order is unknown. + STATE_REASON_QUOTE_ADD = 11; // A quote was added. + STATE_REASON_QUOTE_FULL_EXECUTION = 12; // A quote was fully executed. + STATE_REASON_QUOTE_PARTIAL_EXECUTION = 13; // A quote was partially executed. + } + + // Enum describing the actor responsible for an order state change. + enum MarketActor { + MARKET_ACTOR_UNKNOWN = 0; + MARKET_ACTOR_USER = 1; // The user was the actor. + MARKET_ACTOR_MARKET_OPERATOR = 2; // The market operator was the actor. + MARKET_ACTOR_SYSTEM = 3; // The system was the actor. + } + + // Current state of the order. + OrderState state = 1; + + // Reason for the current state. + StateReason state_reason = 2; + + // Actor responsible for the current state. + MarketActor market_actor = 3; + } + + // Unique identifier of the order. + uint64 order_id = 1; + + // The details of the order. + Order order = 2; + + // Details of the order's current state. + StateDetail state_detail = 3; + + // Remaining open quantity for this order. + frequenz.api.common.v1.markets.Energy open_quantity = 4; + + // Filled quantity for this order. + frequenz.api.common.v1.markets.Energy filled_quantity = 5; + + // UTC Timestamp when the order was created. + google.protobuf.Timestamp create_time = 6; + + // UTC Timestamp of the last update to the order. + google.protobuf.Timestamp modification_time = 7; +} + +// Represents a public order in the market. +// +// Each PublicOrder within this response message represents an order that was previously +// active in the public order book, along with its key attributes and final state. +message PublicOrder { + // ID of the order from the public order book. + uint64 public_order_id = 1; + + // Delivery area code of the buy side. + frequenz.api.common.v1.grid.DeliveryArea buy_delivery_area = 2; + + // Delivery area code of the sell side. + frequenz.api.common.v1.grid.DeliveryArea sell_delivery_area = 3; + + // The delivery period for the contract. + frequenz.api.common.v1.grid.DeliveryPeriod delivery_period = 4; + + // UTC Timestamp of the last order update or matching. + google.protobuf.Timestamp modification_time = 5; + + // The limit price at which the contract is to be traded. + frequenz.api.common.v1.markets.Price price = 6; + + // The quantity of the contract being traded in MWh. + frequenz.api.common.v1.markets.Energy quantity = 7; + + // State of the order. + OrderState state = 8; +} + +// Parameters for filtering Gridpool orders. +// +// !!! note +// Multiple filters can be used in combination to narrow down the returned results. +// For example, you can apply both state and side filters simultaneously +// to list only the open orders on the buy side of the market. +message GridpoolOrderFilter { + // Optional filter for order state. + repeated OrderState state = 1; + + // Optional filter for order side. + optional MarketSide side = 2; + + // Optional filter for delivery period. + optional frequenz.api.common.v1.grid.DeliveryPeriod delivery_period = 3; + + // Optional filter for delivery area. + optional frequenz.api.common.v1.grid.DeliveryArea delivery_area = 4; + + // Optional filters the listed orders by their associated tag. + optional string tag = 5; +} + +// Parameters for filtering the historic, publicly executed orders (trades). +message PublicOrderFilter { + // If set, only orders in this state are returned. + repeated OrderState state = 1; + + // If set, only orders with this delivery period are returned. + optional frequenz.api.common.v1.grid.DeliveryPeriod delivery_period = 2; + + // If set, only orders in this buy delivery area are returned. + optional frequenz.api.common.v1.grid.DeliveryArea buy_delivery_area = 3; + + // If set, only orders in this sell delivery area are returned. + optional frequenz.api.common.v1.grid.DeliveryArea sell_delivery_area = 4; +} + +// Represents a request to create a new order for a specific Gridpool. +// +// A Gridpool is a collection of microgrids that can span multiple delivery areas. It's important to note +// that when submitting an order for a Gridpool, you must group microgrids by their respective delivery +// areas. This means you should submit separate orders for each delivery area within the Gridpool. +// Failing to do so could lead to inaccuracies in the bidding process and may not fully represent the +// capabilities or constraints of the microgrids in different delivery areas. However, in most countries +// only one delivery area exists, exceptions are countries like e.g. Australia, Germany and the US. +// +// !!! caution +// If a Gridpool contains microgrids in both Delivery Area A and Delivery Area B, you should submit +// one order for Delivery Area A and another for Delivery Area B, specifying the details for each. +// +message CreateGridpoolOrderRequest { + // Unique identifier for the Gridpool to which the bid belongs. + uint64 gridpool_id = 1; + + // The details of the order being placed. + Order order = 2; +} + +// Represents the server's response after a new order has been successfully created for a specific Gridpool. +// +// This response provides essential details about the newly created order, such as the unique order ID and +// the state of the order. By receiving this response, users can be sure that their order has been placed +// successfully for the designated Gridpool. +message CreateGridpoolOrderResponse { + // ID of the gridpool the order belongs to. + uint64 gridpool_id = 1; + + // Details of the newly created order. + OrderDetail order_detail = 2; +} + +// Request to update an existing order for a given Gridpool. +message UpdateGridpoolOrderRequest { + // Represents the order properties that can be updated after an order has been placed. + // At least one of the optional fields must be set for an update to take place. + message UpdateOrder { + // The updated limit price at which the contract is to be traded. + // This is the maximum price for a BUY order or the minimum price for a SELL order. + optional frequenz.api.common.v1.markets.Price price = 2; + + // The updated quantity of the contract being traded, specified in MWh. + optional frequenz.api.common.v1.markets.Energy quantity = 3; + + // Applicable for STOP_LIMIT orders. This is the updated stop price that triggers the + // limit order. + optional frequenz.api.common.v1.markets.Price stop_price = 4; + + // Applicable for ICEBERG orders. This is the updated price difference between the + // peak price and the limit price. + optional frequenz.api.common.v1.markets.Price peak_price_delta = 5; + + // Applicable for ICEBERG orders. This is the updated quantity of the order to be + // displayed in the order book. + optional frequenz.api.common.v1.markets.Energy display_quantity = 6; + + // Updated execution options such as All or None, Fill or Kill, etc. + optional OrderExecutionOption execution_option = 7; + + // This is an updated timestamp defining the time after which the order should be + // cancelled if not filled. The timestamp is in UTC. + google.protobuf.Timestamp valid_until = 8; + + // Updated user-defined payload individual to a specific order. + // This can be any data that the user wants to associate with the order. + google.protobuf.Struct payload = 9; + + // Updated user-defined tag to group related orders. + optional string tag = 10; + } + + // ID of the Gridpool the order belongs to. + uint64 gridpool_id = 1; + + // The order identifier + uint64 order_id = 2; + + // Field mask specifying which fields should be updated + google.protobuf.FieldMask update_mask = 3; + + // The fields that can be updated + UpdateOrder update_order_fields = 4; +} + +/ Response from updating an existing order for a given Gridpool. +message UpdateGridpoolOrderResponse { + // ID of the Gridpool the order belongs to. + uint64 gridpool_id = 1; + + // Details of the updated order. + OrderDetail order_detail = 2; +} + +// Request to cancel an existing order for a given Gridpool. +message CancelGridpoolOrderRequest { + // ID of the gridpool the order belongs to. + uint64 gridpool_id = 1; + + // ID of the order to be cancelled. + uint64 order_id = 2; +} + +// Response from canceling an existing order for a given Gridpool. +message CancelGridpoolOrderResponse { + // ID of the gridpool the order belongs to. + uint64 gridpool_id = 1; + + // Details of the cancelled order. + OrderDetail order_detail = 2; +} + +// Request to cancel all currently open orders for a given Gridpool. +message CancelAllGridpoolOrdersRequest { + // ID of the gridpool containing the orders to be cancelled. + uint64 gridpool_id = 1; +} + +// Response to canceling all currently open orders for a given Gridpool. +message CancelAllGridpoolOrdersResponse { + // ID of the gridpool to cancel the orders for. + uint64 gridpool_id = 1; +} + +// Request to retrieve a single order for a given Gridpool. +message GetGridpoolOrderRequest { + // ID of the gridpool containing the order to be retrieved. + uint64 gridpool_id = 1; + + // ID of the order to be retrieved. + uint64 order_id = 2; +} + +// Response from requesting order details for a specific order. +message GetGridpoolOrderResponse { + // ID of the gridpool containing the retrieved order. + uint64 gridpool_id = 1; + + // Details of the retrieved order. + OrderDetail order_detail = 2; +} + +// Request to retrieve a list of orders for a specific Gridpool. +message ListGridpoolOrdersRequest { + // The Gridpool to retrieve the orders for. + uint64 gridpool_id = 1; + + // Optional Gridpool orders filter. + GridpoolOrderFilter filter = 2; + + // Pagination parameters. + frequenz.api.common.v1.pagination.PaginationParams pagination_params = 3; +} + +// Response from listing orders for a given Gridpool. +message ListGridpoolOrdersResponse { + // List of all listed orders with their details. + repeated OrderDetail order_detail_list = 1; + + // Metadata for pagination, including token for next page to retrieve. + frequenz.api.common.v1.pagination.PaginationInfo pagination_info = 2; +} + +// Subscribe to Gridpool order stream. +// This method provides real-time updates on Gridpool orders, making it useful +// for dynamic analytics and real-time decision-making. +message ReceiveGridpoolOrdersStreamRequest { + // The gridpool to retrieve the orders for. + uint64 gridpool_id = 1; + + // Optional public orders filter. + // + // !!! Important Note Regarding "DeliveryPeriod Filter" + // Ensure that the specified DeliveryPeriod is set for a future timeframe. + // If a past or present period is selected, the stream will automatically close + // and return no entries. + GridpoolOrderFilter filter = 2; +} + +// Response to a subscription request for a stream of Gridpool orders. +// Real-time information on gridpool orders is pushed through this response. +message ReceiveGridpoolOrdersStreamResponse { + // Order detail response. + OrderDetail order_detail = 1; +} + +// Request to list all executed public orders with optional filters. +// This method allows for querying historical data, useful for various analytics tasks. +message ListPublicOrdersRequest { + // Optional filter to narrow down the list of public orders. + PublicOrderFilter filter = 1; + + // Pagination parameters. + frequenz.api.common.v1.pagination.PaginationParams pagination_params = 3; +} + +// ListPublicOrdersResponse is a message that contains a list of historic executed public orders. +// This dataset is vital for tasks such as training machine learning models, backtesting trading strategies, +// and conducting market analysis. +message ListPublicOrdersResponse { + // List of all public orders that met the specified filtering criteria. + repeated PublicOrder public_order_list = 1; + + // Metadata for pagination, including token for next page to retrieve. + frequenz.api.common.v1.pagination.PaginationInfo pagination_info = 2; +} + +// Subscribe to public order stream. +// This method provides real-time updates on newly executed public orders, making it useful +// for dynamic analytics and real-time decision-making. +message ReceivePublicOrdersStreamRequest { + // Optional filter to specify which orders should be included in the stream. + PublicOrderFilter filter = 1; +} + +// Response to a subscription request for a stream of public orders. +// Real-time information on public orders is pushed through this response. +message ReceivePublicOrdersStreamResponse { + // The public order that has been executed and is being broadcasted in real-time. + PublicOrder public_order = 1; +}