def get_latest_candles( cls, account_id, candleSpecifications, units=1, smooth=False, dailyAlignment=17, alignmentTimeZone="America/New_York", weeklyAlignment="Friday", ): url = f"{cls._config['base_url']}/v3/accounts/{account_id}/candles/latest" if dailyAlignment < 0 or dailyAlignment > 23: raise ValueError( "dailyAlignment should be between 0 and 23 (inclusive)") response = RequestSender.send( url, cls._headers, RequestType.GET, params={ "candleSpecifications": candleSpecifications, "units": units, "smooth": smooth, "dailyAlignment": dailyAlignment, "alignmentTimeZone": alignmentTimeZone, "weeklyAlignment": weeklyAlignment, }, ) return (response.json() if response.status_code == 200 else response.raise_for_status())
def create_order( cls, account_id, order_type, units, instrument, time_in_force, position_fill, other_params={}, ): url = f"{cls._config['base_url']}/v3/accounts/{account_id}/orders" data = { "order": { "units": units, "instrument": instrument, "timeInForce": time_in_force, "type": order_type, "positionFill": position_fill, } } data["order"].update(other_params) response = RequestSender.send(url, cls._headers, RequestType.POST, data=data) return (response.json() if response.status_code == 201 else response.raise_for_status())
def get_trades( cls, account_id, ids=None, state="OPEN", instrument=None, count=50, beforeID=None, ): if state not in ["OPEN", "CLOSED", "CLOSE_WHEN_TRADEABLE", "ALL"]: raise ValueError( "state must be a valid TradeStateFilter (OPEN, CLOSED, CLOSE_WHEN_TRADEABLE, or ALL)" ) if count < 1 or count > 500: raise ValueError("count must be positive and not exceed 500") url = f"{cls._config['base_url']}/v3/accounts/{account_id}/trades" params = {"state": state, "count": count} if ids: # required format is csv params["ids"] = ",".join(ids) if instrument: params["instrument"] = instrument if beforeID: params["beforeID"] = beforeID response = RequestSender.send(url, cls._headers, RequestType.GET, params=params) return (response.json() if response.status_code == 200 else response.raise_for_status())
def get_instrument_positions(cls, account_id, instrument): url = ( f"{cls._config['base_url']}/v3/accounts/{account_id}/positions/{instrument}" ) response = RequestSender.send(url, cls._headers, RequestType.GET) return (response.json() if response.status_code == 200 else response.raise_for_status())
def get_positionbook(cls, instrument_name, datetime=None): url = f"{cls._config['base_url']}/v3/instruments/{instrument_name}/positionBook" response = RequestSender.send(url, cls._headers, RequestType.GET, params={"time": datetime}) return response.json( ) if response.status_code == 200 else response.raise_for_status()
def get_candles(cls, instrument_name, candle_params={}): url = f"{cls._config['base_url']}/v3/instruments/{instrument_name}/candles" response = RequestSender.send(url, cls._headers, RequestType.GET, params=candle_params) return response.json( ) if response.status_code == 200 else response.raise_for_status()
def get_account_changes(cls, account_id, since_transaction_id=None): url = f"{cls._config['base_url']}/v3/accounts/{account_id}/changes" response = RequestSender.send( url, cls._headers, RequestType.GET, params={"sinceTransactionID": since_transaction_id}) return response.json( ) if response.status_code == 200 else response.raise_for_status()
def close_instrument_positions(cls, account_id, instrument, long_units=0, short_units=0): if long_units != 0 and short_units != 0: raise Exception("Only long_units or short_units") elif long_units and short_units: raise Exception("No units specified") url = f"{cls._config['base_url']}/v3/accounts/{account_id}/positions/{instrument}/close" data = {"shortUnits": str(short_units)} if short_units else {"longUnits": str(long_units)} response = RequestSender.send(url, cls._headers, RequestType.PUT, data=data) return response.json() if response.status_code == 200 else response.raise_for_status()
def get_orderbook(cls, instrument_name, time=None, df=True): url = f"{cls._config['base_url']}/v3/instruments/{instrument_name}/orderBook" response = RequestSender.send( url, cls._headers, RequestType.GET, params={"time": time} ) if response.status_code == 200: orderbook = response.json()["orderBook"] if not df: return orderbook # orderbook['time'] is UTC time, so it is consistent with the time given in the candles # there is also orderbook['unixTime'], but it seems like it's Singapore time UTC+8 orderbook["time"] = isoparse(orderbook["time"][:-1]) orderbook["price"] = float(orderbook["price"]) orderbook["bucketWidth"] = float(orderbook["bucketWidth"]) orderbook["buckets"] = pd.DataFrame(orderbook["buckets"]).astype(float) return orderbook["buckets"] return response.raise_for_status()
def get_positionbook(cls, instrument_name, time=None, df=True): url = f"{cls._config['base_url']}/v3/instruments/{instrument_name}/positionBook" response = RequestSender.send( url, cls._headers, RequestType.GET, params={"time": time} ) if response.status_code == 200: positionbook = response.json()["positionBook"] if not df: return positionbook positionbook["time"] = isoparse(positionbook["time"][:-1]) positionbook["price"] = float(positionbook["price"]) positionbook["bucketWidth"] = float(positionbook["bucketWidth"]) positionbook["buckets"] = pd.DataFrame(positionbook["buckets"]).astype( float ) return positionbook["buckets"] return response.raise_for_status()
def get_candles(cls, instrument_name, candle_params={}, df=True): url = f"{cls._config['base_url']}/v3/instruments/{instrument_name}/candles" default_params = { "granularity": "S5", "count": 5000, } for k, v in default_params.items(): if k not in candle_params: candle_params[k] = v response = RequestSender.send( url, cls._headers, RequestType.GET, params=candle_params ) if response.status_code == 200: candles = response.json() if not df: return candles candles["candles"] = pd.DataFrame(candles["candles"]) candles["candles"]["time"] = pd.to_datetime( candles["candles"]["time"], unit="s" ) candles["candles"]["unixtime"] = ( candles["candles"]["time"] .astype(str) .apply(lambda x: int(Utils.get_unix_timestamp(x))) ) for price in ["mid", "bid", "ask"]: if price not in candles["candles"].columns: continue candles["candles"][f"{price}.open"] = ( candles["candles"][price].map(lambda x: x["o"]).astype(float) ) candles["candles"][f"{price}.high"] = ( candles["candles"][price].map(lambda x: x["h"]).astype(float) ) candles["candles"][f"{price}.low"] = ( candles["candles"][price].map(lambda x: x["l"]).astype(float) ) candles["candles"][f"{price}.close"] = ( candles["candles"][price].map(lambda x: x["c"]).astype(float) ) del candles["candles"][price] return candles["candles"] return response.raise_for_status()
def get_tradeable_instruments(cls, account_id, names_only=False, instruments=[]): if instruments and names_only: raise Exception( "names_only must be False if instruments is not empty") url = f"{cls._config['base_url']}/v3/accounts/{account_id}/instruments" response = RequestSender.send(url, cls._headers, RequestType.GET, params={"instruments": instruments}) if response.status_code != 200: response.raise_for_status() if not names_only: return response.json() else: return [ instrument["name"] for instrument in response.json()["instruments"] ]
def get_pricing( cls, account_id, instruments, since=None, includeUnitsAvailable=True, includeHomeConversions=False, ): url = f"{cls._config['base_url']}/v3/accounts/{account_id}/pricing" response = RequestSender.send( url, cls._headers, RequestType.GET, params={ "instruments": ",".join(instruments), "since": since, "includeUnitsAvailable": includeUnitsAvailable, "includeHomeConversions": includeHomeConversions, }, ) return (response.json() if response.status_code == 200 else response.raise_for_status())
def get_open_positions(cls, account_id): url = f"{cls._config['base_url']}/v3/accounts/{account_id}/openPositions" response = RequestSender.send(url, cls._headers, RequestType.GET) return response.json() if response.status_code == 200 else response.raise_for_status()
def get_specific_trade(cls, account_id, tradeSpecifier): # tradeSpecifier is either TradeID or @ClientID url = f"{cls._config['base_url']}/v3/accounts/{account_id}/trades/{tradeSpecifier}" response = RequestSender.send(url, cls._headers, RequestType.GET) return (response.json() if response.status_code == 200 else response.raise_for_status())
def get_pending_orders(cls, account_id): url = f"{cls._config['base_url']}/v3/accounts/{account_id}/pendingOrders" response = RequestSender.send(url, cls._headers, RequestType.GET) return (response.json() if response.status_code == 200 else response.raise_for_status())
def get_accounts(cls): url = f"{cls._config['base_url']}/v3/accounts" response = RequestSender.send(url, cls._headers, RequestType.GET) return response.json( ) if response.status_code == 200 else response.raise_for_status()