From 8ee56b0e371fab1247587986738a2a399c09906a Mon Sep 17 00:00:00 2001 From: Mark Veidemanis Date: Mon, 31 Oct 2022 08:58:08 +0000 Subject: [PATCH] Begin implementing pydantic validation for OANDA --- core/exchanges/alpaca.py | 4 +- core/exchanges/oanda.py | 23 ++++++++++- core/lib/schemas/alpaca_s.py | 5 +++ core/lib/schemas/oanda_s.py | 78 ++++++++++++++++++++++++++++++++++++ 4 files changed, 106 insertions(+), 4 deletions(-) diff --git a/core/exchanges/alpaca.py b/core/exchanges/alpaca.py index a6c2ffd..a8ae597 100644 --- a/core/exchanges/alpaca.py +++ b/core/exchanges/alpaca.py @@ -57,7 +57,7 @@ class AlpacaExchange(BaseExchange): return (True, balance) - def get_market_value(self, symbol): + def get_market_value(self, symbol): # TODO: pydantic try: position = self.client.get_position(symbol) except APIError as e: @@ -65,7 +65,7 @@ class AlpacaExchange(BaseExchange): return False return float(position["market_value"]) - def post_trade(self, trade): + def post_trade(self, trade): # TODO: pydantic # the trade is not placed yet if trade.direction == "buy": direction = OrderSide.BUY diff --git a/core/exchanges/oanda.py b/core/exchanges/oanda.py index 6f284d1..9f08da6 100644 --- a/core/exchanges/oanda.py +++ b/core/exchanges/oanda.py @@ -4,10 +4,25 @@ from oandapyV20.endpoints import accounts, orders, positions, trades from core.exchanges import BaseExchange from core.lib.schemas import oanda_s -OANDA_SCHEMA_MAPPING = {} +OANDA_SCHEMA_MAPPING = {"OpenPositions": oanda_s.OpenPositions} class OANDAExchange(BaseExchange): + def call(self, method, request): + self.client.request(request) + response = request.response + if isinstance(response, list): + response = {"itemlist": response} + if method not in self.schema: + self.log.error(f"Method cannot be validated: {method}") + self.log.debug(f"Response: {response}") + return (False, f"Method cannot be validated: {method}") + try: + return (True, self.schema[method](**response).dict()) + except ValidationError as e: + self.log.error(f"Could not validate response: {e}") + return (False, e) + def set_schema(self): self.schema = OANDA_SCHEMA_MAPPING @@ -58,5 +73,9 @@ class OANDAExchange(BaseExchange): def get_all_positions(self): r = positions.OpenPositions(accountID=self.account_id) - self.client.request(r) + success, response = self.call("OpenPositions", r) + if not success: + return (success, response) + + print("Positions", response) return (True, []) diff --git a/core/lib/schemas/alpaca_s.py b/core/lib/schemas/alpaca_s.py index a76e0d9..d7a5b9e 100644 --- a/core/lib/schemas/alpaca_s.py +++ b/core/lib/schemas/alpaca_s.py @@ -1,5 +1,6 @@ from pydantic import BaseModel, Field + class Asset(BaseModel): id: str class_: str = Field(..., alias="class") @@ -17,10 +18,12 @@ class Asset(BaseModel): min_trade_increment: str price_increment: str + # get_all_assets class GetAllAssets(BaseModel): itemlist: list[Asset] + # get_open_position class GetOpenPosition(BaseModel): asset_id: str @@ -63,10 +66,12 @@ class Position(BaseModel): change_today: str qty_available: str + # get_all_positions class GetAllPositions(BaseModel): itemlist: list[Position] + # get_account class GetAccount(BaseModel): id: str diff --git a/core/lib/schemas/oanda_s.py b/core/lib/schemas/oanda_s.py index 22145a4..981db4f 100644 --- a/core/lib/schemas/oanda_s.py +++ b/core/lib/schemas/oanda_s.py @@ -1 +1,79 @@ from pydantic import BaseModel + +a = { + "positions": [ + { + "instrument": "EUR_USD", + "long": { + "units": "1", + "averagePrice": "0.99361", + "pl": "-0.1014", + "resettablePL": "-0.1014", + "financing": "0.0000", + "dividendAdjustment": "0.0000", + "guaranteedExecutionFees": "0.0000", + "tradeIDs": ["71"], + "unrealizedPL": "-0.0002", + }, + "short": { + "units": "0", + "pl": "0.0932", + "resettablePL": "0.0932", + "financing": "0.0000", + "dividendAdjustment": "0.0000", + "guaranteedExecutionFees": "0.0000", + "unrealizedPL": "0.0000", + }, + "pl": "-0.0082", + "resettablePL": "-0.0082", + "financing": "0.0000", + "commission": "0.0000", + "dividendAdjustment": "0.0000", + "guaranteedExecutionFees": "0.0000", + "unrealizedPL": "-0.0002", + "marginUsed": "0.0286", + } + ], + "lastTransactionID": "71", +} + + +class PositionLong(BaseModel): + units: str + averagePrice: str + pl: str + resettablePL: str + financing: str + dividendAdjustment: str + guaranteedExecutionFees: str + tradeIDs: list[str] + unrealizedPL: str + + +class PositionShort(BaseModel): + units: str + pl: str + resettablePL: str + financing: str + dividendAdjustment: str + guaranteedExecutionFees: str + unrealizedPL: str + + +class Position(BaseModel): + instrument: str + long: PositionLong + short: PositionShort + pl: str + resettablePL: str + financing: str + commission: str + dividendAdjustment: str + guaranteedExecutionFees: str + unrealizedPL: str + marginUsed: str + + +class OpenPositions(BaseModel): + positions: list[Position] + lastTransactionID: str