使用Oanda API调用XAG等非外汇品种时出现price报错问题求解
Oanda API交易策略自动化XAG品种报错问题
我正在使用Oanda API实现交易策略自动化,遇到price报错问题:该报错仅在选择XAG(白银)等部分交易品种时触发,选择外汇交易对时无异常。我推测问题源于品种分类差异,但目前尚未收到Oanda官方回复。我位于英国,可使用包括CFD在内的大部分交易产品。若有相关解决经验恳请分享。
相关实现代码
import pandas as pd import numpy as np import time from datetime import datetime, timedelta import tpqoa class SMABollTrader(tpqoa.tpqoa): def __init__(self, conf_file, instrument, bar_length, SMA, dev, SMA_S, SMA_L, units): super().__init__(conf_file) self.instrument = instrument self.bar_length = pd.to_timedelta(bar_length) self.tick_data = pd.DataFrame() self.raw_data = None self.data = None self.last_bar = None self.units = units self.position = 0 self.profits = [] self.price = [] # 策略专属参数 self.SMA = SMA self.dev = dev self.SMA_S = SMA_S self.SMA_L = SMA_L def get_most_recent(self, days = 5): while True: time.sleep(2) now = datetime.utcnow() now = now - timedelta(microseconds = now.microsecond) past = now - timedelta(days = days) df = self.get_history(instrument = self.instrument, start = past, end = now, granularity = "S5", price = "M", localize = False).c.dropna().to_frame() df.rename(columns = {"c":self.instrument}, inplace = True) df = df.resample(self.bar_length, label = "right").last().dropna().iloc[:-1] self.raw_data = df.copy() self.last_bar = self.raw_data.index[-1] if pd.to_datetime(datetime.utcnow()).tz_localize("UTC") - self.last_bar < self.bar_length: break def on_success(self, time, bid, ask): print(self.ticks, end = " ") recent_tick = pd.to_datetime(time) df = pd.DataFrame({self.instrument:(ask + bid)/2}, index = [recent_tick]) self.tick_data = pd.concat([self.tick_data, df]) if recent_tick - self.last_bar > self.bar_length: self.resample_and_join() self.define_strategy() self.execute_trades() def resample_and_join(self): self.raw_data = pd.concat([self.raw_data, self.tick_data.resample(self.bar_length, label="right").last().ffill().iloc[:-1]]) self.tick_data = self.tick_data.iloc[-1:] self.last_bar = self.raw_data.index[-1] def define_strategy(self): df = self.raw_data.copy() df["SMA"] = df[self.instrument].rolling(self.SMA).mean() df["Lower"] = df["SMA"] - df[self.instrument].rolling(self.SMA).std() * self.dev df["Upper"] = df["SMA"] + df[self.instrument].rolling(self.SMA).std() * self.dev df["distance"] = df[self.instrument] - df.SMA df["SMA_S"] = df[self.instrument].rolling(self.SMA_S).mean() df["SMA_L"] = df[self.instrument].rolling(self.SMA_L).mean() # 原逻辑语法修正 df["position"] = np.where((df[self.instrument] < df.Lower) & (df["SMA_S"] > df["SMA_L"]), 1, np.nan) df["position"] = np.where((df[self.instrument] > df.Upper) & (df["SMA_S"] < df["SMA_L"]), -1, df["position"]) df["position"] = np.where(df.distance * df.distance.shift(1) < 0, 0, df["position"]) df["position"] = df.position.ffill().fillna(0) self.data = df.copy() def execute_trades(self): if self.data["position"].iloc[-1] == 1: if self.position in [0, None]: order = self.create_order(self.instrument, self.units, suppress = True, ret = True) self.report_trade(order, "GOING LONG") elif self.position == -1: order = self.create_order(self.instrument, self.units * 2, suppress = True, ret = True) self.report_trade(order, "GOING LONG") self.position = 1 elif self.data["position"].iloc[-1] == -1: if self.position == 0: order = self.create_order(self.instrument, -self.units, suppress = True, ret = True) self.report_trade(order, "GOING SHORT") elif self.position == 1: order = self.create_order(self.instrument, -self.units * 2, suppress = True, ret = True) self.report_trade(order, "GOING SHORT") self.position = -1 elif self.data["position"].iloc[-1] == 0: if self.position == -1: order = self.create_order(self.instrument, self.units, suppress = True, ret = True) self.report_trade(order, "GOING NEUTRAL") elif self.position == 1: order = self.create_order(self.instrument, -self.units, suppress = True, ret = True) self.report_trade(order, "GOING NEUTRAL") self.position = 0 def report_trade(self, order, going): time = order["time"] units = order["units"] price = order["price"] pl = float(order["pl"]) self.profits.append(pl) cumpl = sum(self.profits) print("\n" + 100* "-") print("{} | {}".format(time, going)) print("{} | units = {} | price = {} | P&L = {} | Cum P&L = {}".format(time, units, price, pl, cumpl)) print(100 * "-" + "\n") if __name__ == "__main__": trader = SMABollTrader("oanda.cfg", "EUR_GBP", "15m", SMA = 82, dev = 4, SMA_S = 38, SMA_L = 135, units = 100000) trader.get_most_recent() trader.stream_data(trader.instrument, stop = None ) if trader.position != 0: # 若存在最终未平仓头寸 close_order = trader.create_order(trader.instrument, units = -trader.position * trader.units, suppress = True, ret = True) trader.report_trade(close_order, "GOING NEUTRAL") trader.signal = 0
修复方案
- 修正品种命名:Oanda平台的白银交易代码不是
XAG,英国地区可交易的白银CFD标准代码为XAG_USD,你可以调用get_instruments()接口拉取你的账户可交易的全量品种列表,确认对应贵金属的准确命名后再传入参数,外汇对命名规则简单无歧义所以不会触发该类错误。 - 替换price参数:贵金属等非外汇品种部分时段不支持
price="M"(中间价)的历史数据查询,可将get_history方法中的price="M"修改为price="B"(买价)或price="A"(卖价),也可以同时拉取买价卖价后自行计算中间价,避免无中间价数据触发的报错。 - 调整下单单位:外汇对的最小交易单位通常为1,而XAG的最小交易单位为1盎司,你当前代码中默认的100000单位远高于XAG的单笔最大下单限制,可通过
get_instruments()接口查询对应品种的最小交易量、精度参数后再设置units值。 - 处理非连续交易时段:外汇是24小时连续交易,而贵金属存在固定休市时段,你resample时间序列时会出现空值,可在resample后增加
dropna()操作过滤空K线,避免后续指标计算异常。 - 修正多条件判断语法:原代码中
np.where() and np.where()的写法完全错误,会返回不符合预期的数组格式,波动更高的贵金属数据更容易触发后续计算报错,上述代码中已修正为&连接多条件的标准写法。
内容的提问来源于stack exchange,提问作者Bigali33
相关产品推荐
相关产品推荐

