Exemple #1
0
        "MARKET",
        "session":
        "NORMAL",
        "duration":
        "DAY",
        "orderStrategyType":
        "SINGLE",
        "orderLegCollection": [{
            "instruction": "BUY",
            "quantity": int(qty),
            "instrument": {
                "symbol": ticker,
                "assetType": "EQUITY"
            }
        }]
    }
    return data


stocks = pd.read_csv(CSV_FILE)
df = pd.DataFrame(stocks)
orders = []
for i in range(len(df)):
    order = build_order(df.loc[i, "Ticker"], df.loc[i, "Qty"])
    orders.append(order)

TDSession.login()

for order in orders:
    order_response = TDSession.place_order(account=ACCOUNT_NUMBER, order=order)
class AmeritradeRebalanceUtils:

    def __init__(self):
        self.session = None
        self.account = None
        self.account_id = None

    def auth(self, credentials_path='./td_state.json', client_path='./td_client_auth.json'):
        with open(client_path) as f:
            data = json.load(f)
        self.session = TDClient(
            client_id=data['client_id'],
            redirect_uri=data['callback_url'],
            credentials_path=credentials_path
        )
        self.session.login()
        
        # assuming only 1 account under management
        self.account = self.session.get_accounts(fields=['positions'])[0]
        self.account_id = self.account['securitiesAccount']['accountId']
        return self.session
    
    def get_portfolio(self):
        positions = self.account['securitiesAccount']['positions']
        portfolio = {}
        for position in positions:
            portfolio[position['instrument']['symbol']] = position['longQuantity']
        return portfolio

    def place_orders_dry_run(self, portfolio_diff: dict):
        result = portfolio_diff.copy()
        prices = self._get_last_prices(result)
        for ticker, qty in portfolio_diff.items():
            round_qty = round(qty)
            abs_rounded_qty = abs(round_qty)
            result[ticker] = {
            'instruction': ('BUY' if qty > 0 else 'SELL'),
            'qty': abs_rounded_qty,
            'money_movement': round_qty*prices[ticker]*-1
            }
        return result

    def place_orders(self, place_orders_dry_run: dict):
        result = []
        for ticker, order in place_orders_dry_run.items():
            res = self.session.place_order(account=self.account_id, order=self._get_market_order_payload(ticker, order['qty'], order['instruction']))
            result.append(res)
        return result

    def _get_market_order_payload(self, ticker, quantity, instruction='BUY'):
        return {
            "orderType": "MARKET",
            "session": "NORMAL",
            "duration": "DAY",
            "orderStrategyType": "SINGLE",
            "orderLegCollection": [
                {
                    "instruction": instruction,
                    "quantity": quantity,
                    "instrument": {
                    "symbol": ticker,
                    "assetType": "EQUITY"
                    }
                }
            ]
        }

    def _get_last_prices(self, portfolio: dict):
        quotes = self.session.get_quotes(instruments=portfolio.keys())
        portfolio_prices = portfolio.copy()
        for ticker, _ in portfolio_prices.items():
            portfolio_prices[ticker] = quotes[ticker]['lastPrice']
        return portfolio_prices
# Login to the session
td_session.login()

# Define the Order.
order_template = buy_limit_enter = {
    "orderType": "LIMIT",
    "session": "NORMAL",
    "duration": "DAY",
    "price": 10.0,
    "orderStrategyType": "SINGLE",
    "orderLegCollection": [
        {
            "instruction": "BUY",
            "quantity": 1,
            "instrument": {
                "symbol": "AAL",
                "assetType": "EQUITY"
            }
        }
    ]
}

# Place the Order.
order_response = td_session.place_order(
    account=ACCOUNT_NUMBER,
    order=order_template
)

# Print the Response.
pprint.pprint(order_response)
Exemple #4
0
# Define the DURATION of the Order - ENUM EXAMPLE.
new_order.order_duration(duration=DURATION.GOOD_TILL_CANCEL)

# Define a new OrderLeg Object.
new_order_leg = OrderLeg()

# Define the ORDER INSTRUCTION - ENUM EXAMPLE.
new_order_leg.order_leg_instruction(instruction=ORDER_INSTRUCTIONS.SELL)

# Define the PRICE - CAN ONLY BE A FLOAT.
new_order_leg.order_leg_price(price=112.50)

# Define the QUANTITY - CAN ONLY BE A INTEGER.
new_order_leg.order_leg_quantity(quantity=10)

# Define the ASSET to be traded - ENUM EXAMPLE -- SYMBOL MUST ALWAYS BE A STRING.
new_order_leg.order_leg_asset(asset_type=ORDER_ASSET_TYPE.EQUITY,
                              symbol='MSFT')

# Once we have built our order leg, we can add it to our OrderObject.
new_order.add_order_leg(order_leg=new_order_leg)

# Create a new session
td_session = TDClient(account_number=ACCOUNT_NUMBER,
                      account_password=ACCOUNT_PASSWORD,
                      consumer_id=CONSUMER_ID,
                      redirect_uri=REDIRECT_URI)

td_session.place_order(account='11111', order=new_order)
Exemple #5
0
class TDAccount(AbstractAccount):
    def valid_scope(self, scope: Scope):
        for code in scope.codes:
            cc = code.split("_")
            symbol = cc[0]
            symbol_type = cc[1]
            if symbol_type != 'STK':
                raise NotImplementedError
            res = self.client.search_instruments(symbol, "symbol-search")
            if not res or len(res) <= 0 or symbol not in res:
                raise RuntimeError("没有查询到资产,code:" + code)
            if res[symbol]['assetType'] != 'EQUITY':
                raise RuntimeError("资产不是股票类型,暂不支持")

    def __init__(self, name: str, initial_cash: float):
        super().__init__(name, initial_cash)

        self.account_id = None
        self.client: TDClient = None
        self.td_orders: Mapping[str, TDOrder] = {}
        self.start_sync_order_thread()

    def with_client(self, client_id, redirect_uri, credentials_path,
                    account_id):
        self.account_id = account_id
        self.client = TDClient(client_id=client_id,
                               redirect_uri=redirect_uri,
                               credentials_path=credentials_path)
        self.client.login()

    @do_log(target_name='下单', escape_params=[EscapeParam(index=0, key='self')])
    @alarm(target='下单', escape_params=[EscapeParam(index=0, key='self')])
    @retry(limit=3)
    def place_order(self, order: Order):
        td_order = TDOrder(order, self.order_callback, self)
        try:
            resp = self.client.place_order(self.account_id, td_order.to_dict())
        except Exception as e:
            order.status = OrderStatus.FAILED
            raise e
        td_order_id = resp.get('order_id')
        order.td_order_id = td_order_id
        self.sync_order(td_order)
        if order.status == OrderStatus.CREATED or order.status == OrderStatus.FAILED:
            if order.status == OrderStatus.CREATED:
                self.cancel_open_order(order)
            raise RuntimeError("place order error")
        self.td_orders[td_order_id] = td_order

    def start_sync_order_thread(self):
        def do_sync():
            while True:
                for td_order_id in self.td_orders.keys():
                    td_order = self.td_orders.get(td_order_id)
                    if td_order.framework_order.status in [
                            OrderStatus.FAILED, OrderStatus.FILLED,
                            OrderStatus.CANCELED
                    ]:
                        # 如果订单到终态,就不需要同步
                        continue
                    try:
                        self.sync_order(td_order)
                    except:
                        import traceback
                        logging.error("{}".format(traceback.format_exc()))
                import time
                time.sleep(0.5)

        threading.Thread(target=do_sync, name="sync td orders").start()

    @alarm(level=AlarmLevel.ERROR,
           target="同步订单",
           escape_params=[EscapeParam(index=0, key='self')])
    def sync_order(self, td_order: TDOrder):
        """
            同步订单状态以及订单执行情况
            :return:
        """
        if not td_order.td_order_id:
            raise RuntimeError("非法的td订单")
        if td_order.framework_order.status in [
                OrderStatus.FAILED, OrderStatus.CANCELED, OrderStatus.FILLED
        ]:
            logging.info("订单已经到终态,不需要同步")
            return
        o: dict = self.client.get_orders(self.account_id, td_order.td_order_id)
        # 更新框架订单的状态
        td_order_status: str = o.get('status')
        if td_order_status in ['ACCEPTED', 'WORKING', 'QUEUED'] and \
                td_order.framework_order.status == OrderStatus.CREATED:
            td_order.framework_order.status = OrderStatus.SUBMITTED
        elif 'PENDING' in td_order_status:
            # pending状态下,不改变状态
            pass
        elif td_order_status == "CANCELED" and td_order.framework_order.status != OrderStatus.CANCELED:
            td_order.framework_order.status = OrderStatus.CANCELED
            self.order_callback.order_status_change(td_order.framework_order,
                                                    None)
        elif td_order_status == 'FILLED':
            # filled的状态在account.order_filled中变更
            pass
        else:
            raise NotImplementedError("无法处理的订单状态:" + td_order_status)

        # 同步执行详情,由于td的执行详情没有惟一标识,所以每次同步到执行详情时,会将旧的执行详情回滚掉,再应用新的执行详情
        executions: List[Dict] = o.get("orderActivityCollection")
        if executions and len(executions) > 0:
            filled_quantity = 0
            total_cost = 0
            # 汇总所有执行详情
            for execution in executions:
                if not execution.get("executionType") == 'FILL':
                    raise NotImplementedError
                execution_legs: List[Dict] = execution.get("executionLegs")
                if len(execution_legs) > 1:
                    raise NotImplementedError
                if execution_legs and len(execution_legs) == 1:
                    execution_leg = execution_legs[0]
                    single_filled_quantity = execution_leg.get("quantity")
                    single_filled_price = execution_leg.get('price')
                    total_cost += single_filled_price * single_filled_quantity
                    filled_quantity += single_filled_quantity
            filled_avg_price = total_cost / filled_quantity
            if filled_quantity > td_order.framework_order.filled_quantity:
                if len(td_order.framework_order.execution_map) > 0:
                    old_version = td_order.framework_order.execution_map.get(
                        "default").version
                    oe = OrderExecution("default", old_version + 1, 0,
                                        filled_quantity, filled_avg_price,
                                        None, None,
                                        td_order.framework_order.direction,
                                        None)
                else:
                    oe = OrderExecution("default", 1, 0, filled_quantity,
                                        filled_avg_price, None, None,
                                        td_order.framework_order.direction,
                                        None)
                self.order_filled(td_order.framework_order, oe)

    def match(self, data):
        raise NotImplementedError

    @do_log(target_name='取消订单',
            escape_params=[EscapeParam(index=0, key='self')])
    @alarm(target='取消订单', escape_params=[EscapeParam(index=0, key='self')])
    @retry(limit=3)
    def cancel_open_order(self, open_order: Order):
        if not open_order.td_order_id or open_order.td_order_id not in self.td_orders:
            raise RuntimeError("没有td订单号")
        if open_order.status == OrderStatus.CANCELED:
            return
        td_order = self.td_orders[open_order.td_order_id]
        self.client.cancel_order(self.account_id, open_order.td_order_id)
        self.sync_order(td_order)
        open_order.status = OrderStatus.CANCELED
        self.order_callback.order_status_change(open_order, self)

    def update_order(self, order: Order, reason):
        if not order.td_order_id or (order.td_order_id not in self.td_orders):
            raise RuntimeError("没有td订单号")
        if not isinstance(order, LimitOrder):
            raise NotImplementedError
        self.cancel_open_order(order)
        new_order = LimitOrder(order.code, order.direction,
                               order.quantity - order.filled_quantity,
                               Timestamp.now(tz='Asia/Shanghai'),
                               order.limit_price, None)
        self.place_order(new_order)

    def start_save_thread(self):
        # 启动账户保存线程,每隔半小时会保存当前账户的操作数据
        def save():
            while True:
                try:
                    logging.info("开始保存账户数据")
                    self.save()
                except:
                    import traceback
                    err_msg = "保存账户失败:{}".format(traceback.format_exc())
                    logging.error(err_msg)
                import time
                time.sleep(30 * 60)

        threading.Thread(name="account_save", target=save).start()