You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

216 lines
7.3 KiB
Python

from oandapyV20 import API
from oandapyV20.endpoints import accounts, orders, positions, pricing, trades
from core.exchanges import BaseExchange, common
from core.util import logs
log = logs.get_logger("oanda")
class OANDAExchange(BaseExchange):
def call_method(self, request):
self.client.request(request)
response = request.response
if isinstance(response, list):
response = {"itemlist": response}
return response
def connect(self):
self.client = API(access_token=self.account.api_secret)
self.account_id = self.account.api_key
def get_account(self):
r = accounts.AccountDetails(self.account_id)
return self.call(r)
def get_instruments(self):
r = accounts.AccountInstruments(accountID=self.account_id)
response = self.call(r)
return response
def get_currencies(self, currencies):
params = {"instruments": ",".join(currencies)}
r = pricing.PricingInfo(accountID=self.account_id, params=params)
response = self.call(r)
return response
def get_supported_assets(self, response=None):
if not response:
response = self.get_instruments()
return [x["name"] for x in response["itemlist"]]
def get_balance(self, return_usd=False):
r = accounts.AccountSummary(self.account_id)
print("r", r)
response = self.call(r)
print("response", response)
balance = float(response["balance"])
print("balance", balance)
currency = response["currency"]
print("currency", currency)
balance_usd = common.to_currency("sell", self.account, balance, currency, "USD")
print("balance_usd", balance_usd)
common.get_balance_hook(
self.account.user.id,
self.account.user.username,
self.account.id,
self.account.name,
balance_usd,
)
print("common.get_balance_hook", common.get_balance_hook)
if return_usd:
print("YES return_usd")
return balance_usd
print("NO return_usd")
return balance
def get_market_value(self, symbol):
raise NotImplementedError
def post_trade(self, trade):
if trade.direction == "sell":
amount = -trade.amount
else:
amount = trade.amount
data = {
"order": {
# "price": "1.5000", - added later
"timeInForce": trade.time_in_force.upper(),
"instrument": trade.symbol,
"units": str(amount),
"type": trade.type.upper(),
"positionFill": "DEFAULT",
}
}
if trade.stop_loss is not None:
data["order"]["stopLossOnFill"] = {
"timeInForce": "GTC",
"price": str(trade.stop_loss),
}
if trade.take_profit is not None:
data["order"]["takeProfitOnFill"] = {"price": str(trade.take_profit)}
if trade.price is not None:
if trade.type == "limit":
data["order"]["price"] = str(trade.price)
elif trade.type == "market":
data["order"]["priceBound"] = str(trade.price)
if trade.trailing_stop_loss is not None:
data["order"]["trailingStopLossOnFill"] = {
"distance": str(trade.trailing_stop_loss),
"timeInForce": "GTC",
}
r = orders.OrderCreate(self.account_id, data=data)
response = self.call(r)
trade.response = response
trade.status = "posted"
trade.order_id = str(int(response["id"]) + 1)
trade.client_order_id = response["requestID"]
trade.save()
return response
def get_trade_precision(self, symbol):
instruments = self.account.instruments
if not instruments:
log.error("No instruments found")
return None
# Extract the information for the symbol
instrument = self.extract_instrument(instruments, symbol)
if not instrument:
log.error(f"Symbol not found: {symbol}")
return None
# Get the required precision
try:
trade_precision = instrument["tradeUnitsPrecision"]
return trade_precision
except KeyError:
log.error(f"Precision not found for {symbol} from {instrument}")
return None
def close_trade(self, trade_id, units=None, symbol=None):
"""
Close a trade.
"""
if not units:
r = trades.TradeClose(accountID=self.account_id, tradeID=trade_id)
return self.call(r)
else:
trade_precision = self.get_trade_precision(symbol)
if trade_precision is None:
log.error(f"Unable to get trade precision for {symbol}")
return None
units = round(units, trade_precision)
data = {
"units": str(units),
}
r = trades.TradeClose(
accountID=self.account_id, tradeID=trade_id, data=data
)
return self.call(r)
def get_trade(self, trade_id):
# OANDA is off by one...
r = trades.TradeDetails(accountID=self.account_id, tradeID=trade_id)
return self.call(r)
def update_trade(self, trade_id, take_profit_price, stop_loss_price):
data = {}
if take_profit_price:
data["takeProfit"] = {"price": str(take_profit_price)}
if stop_loss_price:
data["stopLoss"] = {"price": str(stop_loss_price)}
r = trades.TradeCRCDO(accountID=self.account_id, tradeID=trade_id, data=data)
return self.call(r)
def cancel_trade(self, trade_id):
raise NotImplementedError
def get_position_info(self, symbol):
r = positions.PositionDetails(self.account_id, symbol)
response = self.call(r)
response["account"] = self.account.name
response["account_id"] = self.account.id
return response
def get_all_positions(self):
items = []
r = positions.OpenPositions(accountID=self.account_id)
response = self.call(r)
for item in response["itemlist"]:
item["account"] = self.account.name
item["account_id"] = self.account.id
item["unrealized_pl"] = float(item["unrealized_pl"])
items.append(item)
return items
def get_all_open_trades(self):
r = trades.OpenTrades(accountID=self.account_id)
return self.call(r)["itemlist"]
def close_position(self, side, symbol):
data = {
f"{side}Units": "ALL",
}
r = positions.PositionClose(
accountID=self.account_id, instrument=symbol, data=data
)
response = self.call(r)
return response
def close_all_positions(self):
all_positions = self.get_all_positions()
responses = []
for position in all_positions:
side = position["side"]
symbol = position["symbol"]
data = {
f"{side}Units": "ALL",
}
r = positions.PositionClose(
accountID=self.account_id, instrument=symbol, data=data
)
response = self.call(r)
responses.append(response)
return responses