def __init__(self, commission_rate, commission_multiplier, min_commission): self.commission_rate = commission_rate self.commission_multiplier = commission_multiplier self.commission_map = defaultdict(lambda: min_commission) self.min_commission = min_commission self.context = Context.get_instance()
def __from_create__( cls, order_id, price, amount, side, position_effect, order_book_id, commission=0., tax=0., trade_id=None, close_today_amount=0, frozen_price=0, calendar_dt=None, trading_dt=None ): trade = cls() trade_id = trade_id or next(trade.trade_id_gen) for value in (price, amount, commission, tax, frozen_price): if value != value: raise RuntimeError( "price, amount, commission, tax and frozen_price of trade {trade_id} is not supposed to be nan, current_value is {price}, {amount}, {commission}, {tax}, {frozen_price}" .format( trade_id=trade_id, price=price, amount=amount, commission=commission, tax=tax, frozen_price=frozen_price )) context = Context.get_instance() trade._calendar_dt = calendar_dt or context.calendar_dt trade._trading_dt = trading_dt or context.trading_dt trade._price = price trade._amount = amount trade._order_id = order_id trade._commission = commission trade._tax = tax trade._trade_id = trade_id trade._close_today_amount = close_today_amount trade._side = side trade._position_effect = position_effect trade._order_book_id = order_book_id trade._frozen_price = frozen_price return trade
def _on_settlement(self, event): trading_date = Context.get_instance().trading_dt.date() for order_book_id, positions in list(self._positions.items()): for position in six.itervalues(positions): #print(trading_date) #print(position) delta_cash = position.settlement(trading_date) #print(delta_cash) self._total_cash += delta_cash for order_book_id, positions in list(self._positions.items()): if all(p.quantity == 0 and p.equity == 0 for p in six.itervalues(positions)): del self._positions[order_book_id] self._backward_trade_set.clear() # if total_value <= 0, forced_liquidation forced_liquidation = True #Environment.get_instance().config.base.forced_liquidation if self.total_value <= 0 and forced_liquidation: if self._positions: print("Trigger Forced Liquidation, current total_value is 0") self._positions.clear() self._total_cash = 0
def last_price(self): if self._last_price != self._last_price: context = Context.get_instance() self._last_price = context.get_last_price(self._order_book_id) if self._last_price != self._last_price: raise RuntimeError("last price of position {} is not supposed to be nan".format(self._order_book_id)) return self._last_price
def get_current_trading_dt_total_value(self): # this method is stateless operation total_equity = 0 for p in self._iter_pos(): if p.quantity !=0: price = Context.get_instance().get_last_price(p.order_book_id) total_equity += price * p.quantity return self._total_cash + total_equity
def _update_last_price(self, _): context = Context.get_instance() #print("_update_last_price", context.trading_dt) for order_book_id, positions in self._positions.items(): price = context.get_last_price(order_book_id) if price == price: for position in six.itervalues(positions): #print("before update last price \n {}".format(position)) position.update_last_price(price)
def order_target_quantities(target_quantities: Dict[str, int]) -> List[Order]: """ make the account position to touch the target quantities :param target_quantities: a dictionary contain the target quantities of position :example: .. code-block:: python # adjust positions, to make the '000001.XSHE' to touch the target quantities 800 # make the '000002.XSHE' to touch the target quantities 400 order_target_quantities({ '000001.XSHE': 800 '000002.XSHE': 400 }) """ context = Context.get_instance() account = context.portfolio.accounts[DEFAULT_ACCOUNT_TYPE.STOCK] close_orders, open_orders = [], [] current_quantities = { p.order_book_id: p.quantity for p in account.get_positions() if p.direction == POSITION_DIRECTION.LONG } # close all position if the order_book_id not in the key list of target_quantities for order_book_id, quantity in current_quantities.items(): if order_book_id not in target_quantities: close_orders.append( Order.__from_create__(order_book_id, quantity, SIDE.SELL, MarketOrder(), POSITION_EFFECT.CLOSE)) round_lot = 100 for order_book_id, target_quantity in target_quantities.items(): if order_book_id in current_quantities: delta_quantity = target_quantity - current_quantities[order_book_id] else: delta_quantity = target_quantity if delta_quantity >= round_lot: delta_quantity = math.floor(delta_quantity / round_lot) * round_lot open_orders.append( Order.__from_create__(order_book_id, delta_quantity, SIDE.BUY, MarketOrder(), POSITION_EFFECT.OPEN)) elif delta_quantity < -1: delta_quantity = math.floor(delta_quantity) close_orders.append( Order.__from_create__(order_book_id, abs(delta_quantity), SIDE.SELL, MarketOrder(), POSITION_EFFECT.CLOSE)) to_submit_orders = [] for order in chain(close_orders, open_orders): #print("to submit order: {}".format(order)) to_submit_orders.append(order) return to_submit_orders
def get_positions() -> List[Position]: """ get all available positions, :example: .. code-block:: python3 [In] get_positions() [Out] [BookingPosition({'order_book_id': '000014.XSHE', 'quantity': 100, 'direction': POSITION_DIRECTION.LONG, 'old_quantity': 0, 'trading_pnl': 1.0, 'avg_price': 9.56, 'last_price': 0, 'position_pnl': 0.0}), BookingPosition({'order_book_id': '000010.XSHE', 'quantity': 100, 'direction': POSITION_DIRECTION.LONG, 'old_quantity': 0, 'trading_pnl': 0.0, 'avg_price': 3.09, 'last_price': 0, 'position_pnl': 0.0})] """ portfolio = Context.get_instance().portfolio return portfolio.get_positions()
def test_senond_step_sell(self): to_submit_orders = order_target_weights({self.order_book_id: 0.2}) state, reward, is_done, info = self.env.step(action=to_submit_orders) order = to_submit_orders[0] # this is a sell trade #pdb.set_trace() self.assertEqual(first=order.side, second=SIDE.SELL) # second_trading_dt = self.trading_dts[1] expect_deal_price = self.data_source.get_last_price( order_book_id=self.order_book_id, dt=second_trading_dt) expect_deal_money = expect_deal_price * order.quantity expect_commission_fee = expect_deal_money * self.commission_rate * self.commission_multiplier expect_tax = expect_deal_money * self.tax_rate * self.tax_multiplier # no tax rate when buy expect_transaction_cost = expect_commission_fee + expect_tax print("expect_transaction_cost: {}".format(expect_transaction_cost)) second_trade = Context.get_instance().tracker._trades[1] print(second_trade) self.assertEqual(first=second_trade["order_book_id"], second=self.order_book_id) self.assertEqual( first=second_trade["trading_datetime"], second=second_trading_dt.strftime("%Y-%m-%d %H:%M:%S")) self.assertEqual(first=second_trade["last_price"], second=expect_deal_price) self.assertEqual(first=second_trade["commission"], second=expect_commission_fee) self.assertEqual(first=second_trade["tax"], second=expect_tax) self.assertEqual(first=second_trade["transaction_cost"], second=expect_transaction_cost) expect_cash_stock_settlement = self.expect_cash_stock_settlement + expect_deal_money - expect_transaction_cost next_trading_dt = self.trading_dts[2] expect_settlement_price = self.data_source.get_last_price( order_book_id=self.order_book_id, dt=next_trading_dt) expect_remaining_market_value_stock_settlement = ( self.first_trade["last_quantity"] - second_trade["last_quantity"]) * expect_settlement_price self.expect_total_value_stock_settlement = expect_cash_stock_settlement + expect_remaining_market_value_stock_settlement self.expect_total_value_future_settlement = self.expect_total_value_future expect_total_value_settlement = self.expect_total_value_stock_settlement + self.expect_total_value_future_settlement expect_reward = (expect_total_value_settlement - self.expect_total_value_settlement ) / self.expect_total_value_settlement #pdb.set_trace() self.assertAlmostEqual(first=reward, second=expect_reward)
def is_cash_enough(order, account, warn=True): order_cost = order.frozen_price * order.quantity #instrument.calc_cash_occupation(order.frozen_price, order.quantity, order.position_direction) order_cost += Context.get_instance().get_order_transaction_cost(order) if order_cost <= account.cash: return True if warn: print( "Order Creation Failed: not enough money to buy {order_book_id}, needs {cost_money:.2f}," " cash {cash:.2f}").format( order_book_id=order.order_book_id, cost_money=order_cost, cash=account.cash, ) return False
def register_event(self): event_bus = Context.get_instance().event_bus event_bus.add_listener( EVENT.TRADE, lambda e: self.apply_trade(e.trade, e.order) if e.account == self else None ) event_bus.add_listener(EVENT.ORDER_PENDING_NEW, self._on_order_pending_new) event_bus.add_listener(EVENT.ORDER_CREATION_REJECT, self._on_order_unsolicited_update) event_bus.add_listener(EVENT.ORDER_UNSOLICITED_UPDATE, self._on_order_unsolicited_update) #event_bus.add_listener(EVENT.ORDER_CANCELLATION_PASS, self._on_order_unsolicited_update) event_bus.add_listener(EVENT.PRE_BEFORE_TRADING, self._on_before_trading) event_bus.add_listener(EVENT.SETTLEMENT, self._on_settlement) event_bus.prepend_listener(EVENT.PRE_BAR, self._update_last_price)
def test_trading_dts_setup(self): #test available trading dts all_trading_dts = self.data_source.get_available_trading_dts() expected_available_trading_dts = all_trading_dts[self. look_backward_window - 1:] actual_available_trading_dts = Context.get_instance( ).get_available_trading_dts() self.assertListEqual(list(actual_available_trading_dts), list(expected_available_trading_dts)) #test the first trading dt expected_first_trading_dt = expected_available_trading_dts[0] actual_first_trading_dt = Context.get_instance().trading_dt self.assertEqual(actual_first_trading_dt, expected_first_trading_dt) order_book_ids = self.data_source.get_available_order_book_ids() expected_first_last_price = self.data_source.get_last_price( order_book_id=order_book_ids[0], dt=expected_first_trading_dt) actual_first_last_price = Context.get_instance().get_last_price( order_book_id=order_book_ids[0]) self.assertEqual(actual_first_last_price, expected_first_last_price)
def get_position( order_book_id: str, direction: POSITION_DIRECTION = POSITION_DIRECTION.LONG) -> Position: """ get the position object from one specific order_book_id, :param order_book_id: :param direction: :example: .. code-block:: python3 [In] get_position('000014.XSHE','long_positions") [Out] [BookingPosition({'order_book_id': '000014.XSHE', 'quantity': 100, 'direction': POSITION_DIRECTION.LONG, 'old_quantity': 0, 'trading_pnl': 1.0, 'avg_price': 9.56, 'last_price': 0, 'position_pnl': 0.0})] """ portfolio = Context.get_instance().portfolio return portfolio.get_position(order_book_id, direction)
def _submit_order(order_book_id, amount, side, position_effect, style, quantity, auto_switch_order_value): # param: amount: the target quantity of this order # param: quantity: the quantity of exist position of order_book_id context = Context.get_instance() if isinstance(style, LimitOrder): if not is_valid_price(style.get_limit_price()): raise RuntimeError((u"Limit order price should be positive")) price = context.get_last_price(order_book_id) if not is_valid_price(price): print("Order Creation Failed: [{order_book_id}] No market data" ).format(order_book_id=order_book_id) return round_lot = 100 if side in [SIDE.BUY, side.SELL]: if not (side == SIDE.SELL and quantity == abs(amount)): # KSH can buy(sell) 201, 202 shares amount = int(Decimal(amount) / Decimal(round_lot)) * round_lot if amount == 0: print( "Order Creation Failed: 0 order quantity, order_book_id={order_book_id}" ).format(order_book_id=order_book_id) return order = Order.__from_create__(order_book_id, abs(amount), side, style, position_effect) if order.type == ORDER_TYPE.MARKET: order.set_frozen_price(price) if side == SIDE.BUY and auto_switch_order_value: account, position = _get_account_position_ins(order_book_id) if not is_cash_enough(order, account): print( "insufficient cash, use all remaining cash({}) to create order" ).format(account.cash) return _order_value(account, position, order_book_id, account.cash, style) return order
def _order_value(account, position, order_book_id, cash_amount, style): context = Context.get_instance() if cash_amount > 0: cash_amount = min(cash_amount, account.cash) if isinstance(style, LimitOrder): price = style.get_limit_price() else: price = context.get_last_price(order_book_id) if not is_valid_price(price): print("Order Creation Failed: [{order_book_id}] No market data" ).format(order_book_id=order_book_id) return amount = int(Decimal(cash_amount) / Decimal(price)) round_lot = 100 #int(ins.round_lot) if cash_amount > 0: amount = int(Decimal(amount) / Decimal(round_lot)) * round_lot while amount > 0: expected_transaction_cost = context.get_order_transaction_cost( Order.__from_create__(order_book_id, amount, SIDE.BUY, LimitOrder(price), POSITION_EFFECT.OPEN)) if amount * price + expected_transaction_cost <= cash_amount: break amount -= round_lot else: print("Order Creation Failed: 0 order quantity") return if amount < 0: amount = max(amount, -position.closable) return _order_shares(order_book_id, amount, style, position.quantity, auto_switch_order_value=False)
def __from_create__(cls, order_book_id, quantity, side, style, position_effect, **kwargs): context = Context.get_instance() order = cls() order._order_id = next(order.order_id_gen) order._calendar_dt = context.calendar_dt order._trading_dt = context.trading_dt order._quantity = quantity order._order_book_id = order_book_id order._side = side order._position_effect = position_effect order._message = "" order._filled_quantity = 0 order._status = ORDER_STATUS.PENDING_NEW if isinstance(style, LimitOrder): order._frozen_price = style.get_limit_price() order._type = ORDER_TYPE.LIMIT else: order._frozen_price = 0. order._type = ORDER_TYPE.MARKET order._avg_price = 0 order._transaction_cost = 0 order._kwargs = kwargs return order
#!/usr/bin/env python3 # -*- coding: utf-8 -*- from sharpe.utils.mock_data import create_toy_feature from sharpe.data.data_source import DataSource from sharpe.environment import TradingEnv feature_df, price_s = create_toy_feature(order_book_ids_number=2, feature_number=3, random_seed=111) data_source = DataSource(feature_df=feature_df, price_s=price_s) env = TradingEnv(data_source=data_source, look_backward_window=2) print('--------------------------------------------') from sharpe.core.context import Context print("current context \n", Context.get_instance().__dict__) from sharpe.mod.sys_account.api import order_target_weights to_submit_orders = order_target_weights({"000001.XSHE": 0.5}) state, reward, is_done, info = env.step(action=to_submit_orders) print(state, reward, is_done) # to_submit_orders2 = order_target_portfolio({"000001.XSHE":0.2}) # state, reward, is_done, info = env.step(action=to_submit_orders2) # print(state, reward, is_done)
def test_first_step_buy_and_then_sell(self): feature_df, price_s = create_toy_feature(order_book_ids_number=1, feature_number=3, random_seed=111) data_source = DataSource(feature_df=feature_df, price_s=price_s) order_book_ids = data_source.get_available_order_book_ids() STOCK_INIT_CASH = 1000000 FUTURE_INIT_CASH = 10000 starting_cash = {"STOCK": STOCK_INIT_CASH, "FUTURE": FUTURE_INIT_CASH} commission_rate = 0.0005 #default value in environment tax_rate = 0.001 #default value in environment commission_multiplier = 2 min_commission = 5 tax_multiplier = 1 """ MANUALly check first date: target weight: 0.5 price at end: 77.53 target quantity: 6400 deal_money = 6400 * 77.53 = 496192 commition_fee_total = 496.192 availabel_cash_stock = 1000000 - 496192 - 496.192 = 503311.808 total_value_account_stock = 496192 + 503311.808 = 999503.808 total_value_account = 999503.808 + 10000 = 1009503.808 returns = 1009503.808/1010000 -1 = -0.0004912792079208028 second date: target weight: 0.2 price at end: 69.78 stock_account_at_this_time: 6400*69.78 + 503311.808 = 949903.808 target quantity: 2700 to_trade_quantity: 3700 deal_money = 3700 * 69.78 = 258186 commition_fee_total = 258186 * (0.001 + 0.001) = 516.372 available_cash_stock = 503311.808 + 258186 - 516.372 = 760981.436 total_value_account_stock = 760981.436 + 2700*69.78 = 949387.436 total_value_account = 949387.436 + 10000 =959387.436 return = 959387.436/1009503.808 - 1 = -0.04964455963696568 """ env = TradingEnv(data_source=data_source, look_backward_window=2, mode="non-rl", starting_cash=starting_cash, commission_multiplier=commission_multiplier, min_commission=min_commission, tax_multiplier=tax_multiplier) print('--------------------------------------------') #print("current context \n",Context.get_instance().__dict__) context = Context.get_instance() #stock account expect_market_value_stock = 0 expect_cash_stock = starting_cash["STOCK"] expect_total_value_stock = expect_cash_stock + expect_market_value_stock #future account expect_market_value_future = 0 expect_cash_future = starting_cash["FUTURE"] expect_total_value_future = expect_cash_future + expect_market_value_future expect_total_value_future = expect_total_value_future #portfolio expect_total_value_portfolio = expect_total_value_stock + expect_total_value_future trading_dts = context.get_available_trading_dts() first_trading_dt = trading_dts[0] order_book_id = order_book_ids[0] # target_weight = 0.5 print( "------------the first trading date: {}--------------------------". format(env.trading_dt)) to_submit_orders = order_target_weights({order_book_id: target_weight}) submit_order = to_submit_orders[0] # ============================================================= # # step1: test trade correctness # # ============================================================= # expect_deal_price1 = data_source.get_last_price( order_book_id=order_book_id, dt=first_trading_dt) print("fisrt date end price: {}".format(expect_deal_price1)) expect_quantity1 = (STOCK_INIT_CASH * target_weight) / expect_deal_price1 expect_quantity1 = int(round(expect_quantity1 / 100) * 100) print((submit_order.quantity, expect_quantity1)) expect_deal_money = expect_deal_price1 * expect_quantity1 expect_commission_fee = expect_deal_money * commission_rate * commission_multiplier expect_tax = 0 # no tax rate when buy expect_transaction_cost = expect_commission_fee + expect_tax state, reward, is_done, info = env.step(action=to_submit_orders) true_order = context.tracker._orders[0] print(true_order) first_trade = context.tracker._trades[0] #first_trade = first_trade self.assertEqual(first=first_trade["order_book_id"], second=order_book_id) self.assertEqual(first=first_trade["trading_datetime"], second=first_trading_dt.strftime("%Y-%m-%d %H:%M:%S")) self.assertEqual(first=first_trade["last_price"], second=expect_deal_price1) self.assertEqual(first=first_trade["commission"], second=expect_commission_fee) self.assertEqual(first=first_trade["tax"], second=expect_tax) self.assertEqual(first=first_trade["transaction_cost"], second=expect_transaction_cost) # =========================================== # # step2.1 test position # # step2.2 test account # # step2.3 test portfolio # # =========================================== # # portfolio and accounts change after trading #stock account expect_market_value_stock = expect_deal_money #496192 expect_cash_stock = starting_cash[ "STOCK"] - expect_market_value_stock - expect_transaction_cost #502448.2768 expect_total_value_stock = expect_cash_stock + expect_market_value_stock portfolio = Context.get_instance().portfolio stock_account = portfolio.stock_account position1 = stock_account.get_position(order_book_id, POSITION_DIRECTION.LONG) print(expect_market_value_stock, position1.market_value) #pdb.set_trace() assert expect_market_value_stock == position1.market_value print(expect_market_value_stock, stock_account.market_value) print(expect_cash_stock, stock_account.total_cash) print(expect_total_value_stock, stock_account.total_value) self.assertEqual(expect_market_value_stock, position1.market_value) self.assertEqual(expect_market_value_stock, stock_account.market_value) self.assertEqual(expect_cash_stock, stock_account.total_cash) self.assertEqual(expect_total_value_stock, stock_account.total_value) # #future account expect_market_value_future = 0 expect_cash_future = starting_cash["FUTURE"] expect_total_value_future = expect_cash_future + expect_market_value_future # #portfolio expect_cash_portfolio = expect_cash_stock + expect_cash_future expect_market_value = expect_market_value_stock + expect_market_value_future expect_total_value_portfolio_new = expect_total_value_stock + expect_total_value_future print(expect_cash_portfolio, portfolio.cash) print(expect_market_value, portfolio.market_value) print(expect_total_value_portfolio_new, portfolio.total_value) portfolio_record = Context.get_instance().tracker._total_portfolio[0] #print(portfolio_record) expect_reward = (expect_total_value_portfolio_new / expect_total_value_portfolio) - 1 print(expect_reward, reward, portfolio.daily_returns) #pdb.set_trace() self.assertEqual(expect_cash_portfolio, portfolio.cash) self.assertEqual(expect_market_value, portfolio.market_value) self.assertEqual(expect_total_value_portfolio_new, portfolio.total_value) self.assertEqual(expect_reward, reward) # ============================================================== # # the next dt trade # # ============================================================== # print( "------------the second trading date: {}--------------------------" .format(env.trading_dt)) target_weight = 0.2 second_trading_dt = trading_dts[1] print(second_trading_dt, env.trading_dt) self.assertEqual(second_trading_dt, env.trading_dt) to_submit_orders = order_target_weights({order_book_id: target_weight}) order = to_submit_orders[0] #pdb.set_trace() expect_deal_price = data_source.get_last_price( order_book_id=order_book_id, dt=second_trading_dt) print("second date end price: {}".format(expect_deal_price)) #here is the point: we need to get the updated account total value expect_deal_price = data_source.get_last_price( order_book_id=order_book_id, dt=second_trading_dt) #69.78 expect_market_value_stock = expect_quantity1 * expect_deal_price expect_cash_stock = expect_cash_stock expect_total_value_stock = expect_cash_stock + expect_market_value_stock expect_quantity2 = (expect_total_value_stock * target_weight) / expect_deal_price expect_quantity2 = int(round(expect_quantity2 / 100) * 100) to_trade_quantity = abs(expect_quantity2 - expect_quantity1) #expect_quantity = true_order.quantity #allow not 100 times #print((, expetrue_orderct_quantity)) expect_deal_money = expect_deal_price * to_trade_quantity expect_commission_fee = expect_deal_money * commission_rate * commission_multiplier expect_tax = expect_deal_money * tax_rate * tax_multiplier # no tax rate when buy, but charge when sell expect_transaction_cost = expect_commission_fee + expect_tax #pdb.set_trace() state, reward, is_done, info = env.step(action=to_submit_orders) true_order = context.tracker._orders[1] # if sell, allow the quantity is not 100 times print(true_order) second_trade = context.tracker._trades[1] print(second_trade["order_book_id"], order_book_id) print(second_trade["trading_datetime"], second_trading_dt.strftime("%Y-%m-%d %H:%M:%S")) print(second_trade["last_price"], expect_deal_price) print(second_trade["commission"], expect_commission_fee) print(second_trade["tax"], expect_tax) print(second_trade["transaction_cost"], expect_transaction_cost) self.assertEqual(first=second_trade["order_book_id"], second=order_book_id) self.assertEqual( first=second_trade["trading_datetime"], second=second_trading_dt.strftime("%Y-%m-%d %H:%M:%S")) self.assertEqual(first=second_trade["last_price"], second=expect_deal_price) self.assertEqual(first=second_trade["commission"], second=expect_commission_fee) self.assertEqual(first=second_trade["tax"], second=expect_tax) self.assertEqual(first=second_trade["transaction_cost"], second=expect_transaction_cost) #pdb.set_trace() # here is important and think why use settlement price rather the trade price # the special case. when sell at the end time of the trading time. the settlement price is equal to the trade price expect_cash_stock_settlement = expect_cash_stock + expect_deal_money - expect_transaction_cost expect_settlement_price = data_source.get_last_price( order_book_id=order_book_id, dt=second_trading_dt) position1 = stock_account.get_position(order_book_id, POSITION_DIRECTION.LONG) print(position1) #expect_remaining_market_value_stock_settlement = (first_trade["last_quantity"] - second_trade["last_quantity"]) * expect_settlement_price expect_market_value_stock_settlement = position1.quantity * expect_settlement_price expect_total_value_stock_settlement = expect_cash_stock_settlement + expect_market_value_stock_settlement portfolio = Context.get_instance().portfolio stock_account = portfolio.stock_account position1 = stock_account.get_position(order_book_id, POSITION_DIRECTION.LONG) #expect_reward = (expect_total_value_settlement - self.expect_total_value_settlement) / self.expect_total_value_settlement print(expect_market_value_stock_settlement, position1.market_value) print(expect_market_value_stock_settlement, stock_account.market_value) print(expect_cash_stock_settlement, stock_account.total_cash) print(expect_total_value_stock_settlement, stock_account.total_value) #pdb.set_trace() self.assertEqual(expect_market_value_stock_settlement, position1.market_value) self.assertEqual(expect_market_value_stock_settlement, stock_account.market_value) self.assertEqual(expect_cash_stock_settlement, stock_account.total_cash) self.assertEqual(expect_total_value_stock_settlement, stock_account.total_value) expect_cash_future_settlement = starting_cash["FUTURE"] expect_market_value_future_settlement = 0 expect_total_value_future_settlement = expect_cash_future_settlement + expect_market_value_future_settlement expect_cash_portfolio_settlement = expect_cash_stock_settlement + expect_cash_future_settlement expect_market_value_portfolio_settlement = expect_market_value_stock_settlement + expect_market_value_future_settlement expect_total_value_settlement_portfolio_2 = expect_total_value_stock_settlement + expect_total_value_future_settlement #pdb.set_trace() print(expect_cash_portfolio_settlement, portfolio.cash) print(expect_market_value_portfolio_settlement, portfolio.market_value) print(expect_total_value_settlement_portfolio_2, portfolio.total_value) expect_reward = (expect_total_value_settlement_portfolio_2 / expect_total_value_portfolio_new) - 1 print(expect_reward, reward, portfolio.daily_returns) #pdb.set_trace() self.assertEqual(expect_cash_portfolio_settlement, portfolio.cash) self.assertEqual(expect_market_value_portfolio_settlement, portfolio.market_value) self.assertEqual(expect_total_value_settlement_portfolio_2, portfolio.total_value) self.assertEqual(expect_reward, reward) print( "------------the third trading date: {}--------------------------". format(env.trading_dt)) third_trading_dt = trading_dts[2] print(second_trading_dt, env.trading_dt) target_weight = 0.0 to_submit_orders = order_target_weights({order_book_id: target_weight}) expect_deal_price = data_source.get_last_price( order_book_id=order_book_id, dt=third_trading_dt) print("third date end price: {}".format(expect_deal_price)) #72.5 expect_quantity = 0 #allow not 100 times to_trade_quantity = expect_quantity2 # #print((, expetrue_orderct_quantity)) expect_deal_money = expect_deal_price * to_trade_quantity expect_commission_fee = expect_deal_money * commission_rate * commission_multiplier expect_tax = expect_deal_money * tax_rate * tax_multiplier # no tax rate when buy expect_transaction_cost = expect_commission_fee + expect_tax state, reward, is_done, info = env.step(action=to_submit_orders) order = to_submit_orders[0] true_order = context.tracker._orders[2] # if sell, allow the quantity is not 100 times print(true_order) third_trade = context.tracker._trades[2] print(third_trade["order_book_id"], order_book_id) print(third_trade["trading_datetime"], second_trading_dt.strftime("%Y-%m-%d %H:%M:%S")) print(third_trade["last_price"], expect_deal_price) print(third_trade["commission"], expect_commission_fee) print(third_trade["tax"], expect_tax) print(third_trade["transaction_cost"], expect_transaction_cost) #pdb.set_trace() self.assertEqual(first=third_trade["order_book_id"], second=order_book_id) self.assertEqual(first=third_trade["trading_datetime"], second=third_trading_dt.strftime("%Y-%m-%d %H:%M:%S")) self.assertEqual(first=third_trade["last_price"], second=expect_deal_price) self.assertEqual(first=third_trade["commission"], second=expect_commission_fee) self.assertEqual(first=third_trade["tax"], second=expect_tax) self.assertEqual(first=third_trade["transaction_cost"], second=expect_transaction_cost) # # here is important and think why use settlement price rather the trade price # # the special case. when sell at the end time of the trading time. the settlement price is equal to the trade price expect_cash_stock_settlement = expect_cash_stock_settlement + expect_deal_money - expect_transaction_cost expect_settlement_price = data_source.get_last_price( order_book_id=order_book_id, dt=third_trading_dt) position = stock_account.get_position(order_book_id, POSITION_DIRECTION.LONG) print(position) expect_market_value_stock_settlement = position.quantity * expect_settlement_price print("expect_market_value_stock_settlement: {}".format( expect_market_value_stock_settlement)) #expect_remaining_market_value_stock_settlement = (first_trade["last_quantity"] - second_trade["last_quantity"]) * expect_settlement_price expect_total_value_stock_settlement = expect_cash_stock_settlement + expect_market_value_stock_settlement portfolio = Context.get_instance().portfolio stock_account = portfolio.stock_account position1 = stock_account.get_position(order_book_id, POSITION_DIRECTION.LONG) print(expect_market_value_stock_settlement, position1.market_value) print(expect_market_value_stock_settlement, stock_account.market_value) print("---------------------") print(expect_cash_stock_settlement, stock_account.total_cash) print(expect_total_value_stock_settlement, stock_account.total_value) #pdb.set_trace() self.assertEqual(expect_market_value_stock_settlement, position1.market_value) self.assertEqual(expect_market_value_stock_settlement, stock_account.market_value) self.assertEqual(expect_cash_stock_settlement, stock_account.total_cash) self.assertEqual(expect_total_value_stock_settlement, stock_account.total_value) expect_cash_future_settlement = starting_cash["FUTURE"] expect_market_value_future_settlement = 0 expect_total_value_future_settlement = expect_cash_future_settlement + expect_market_value_future_settlement expect_cash_portfolio_settlement = expect_cash_stock_settlement + expect_cash_future_settlement expect_market_value_portfolio_settlement = expect_market_value_stock_settlement + expect_market_value_future_settlement expect_total_value_settlement_portfolio = expect_total_value_stock_settlement + expect_total_value_future_settlement print(expect_cash_portfolio_settlement, portfolio.cash) print(expect_market_value_portfolio_settlement, portfolio.market_value) print(expect_total_value_settlement_portfolio, portfolio.total_value) expect_reward = (expect_total_value_settlement_portfolio / expect_total_value_settlement_portfolio_2) - 1 print(expect_reward, reward, portfolio.daily_returns) print(portfolio.cash, portfolio.market_value, portfolio.total_value) #pdb.set_trace() self.assertEqual(expect_cash_portfolio_settlement, portfolio.cash) self.assertEqual(expect_market_value_portfolio_settlement, portfolio.market_value) self.assertEqual(expect_total_value_settlement_portfolio, portfolio.total_value) self.assertEqual(expect_reward, reward)
def reset(self): state = Context.get_instance().history_bars() return state
def test_portfolio_setup(self): portfolio = Context.get_instance().portfolio expected_total_value = sum(self.starting_cash.values()) actual_total_value = portfolio.total_value self.assertEqual(actual_total_value, expected_total_value)
def setUp(self): #feature_df, price_s = create_toy_feature(order_book_ids_number=2, feature_number=3, start="2020-01-01", end="2020-01-11", random_seed=111) feature_df, price_s = create_toy_feature(order_book_ids_number=1, feature_number=3, random_seed=111) data_source = DataSource(feature_df=feature_df, price_s=price_s) #order_book_ids = data_source.get_available_order_book_ids() self.data_source = DataSource(feature_df=feature_df, price_s=price_s) self.order_book_ids = self.data_source.get_available_order_book_ids() self.look_backward_window = 2 self.starting_cash = {"STOCK": 1000000, "FUTURE": 10000} self.commission_rate = 0.0005 self.tax_rate = 0.001 self.commission_multiplier = 1 self.min_commission = 5 self.tax_multiplier = 1 self.env = TradingEnv(data_source=self.data_source, look_backward_window=self.look_backward_window, mode="rl", starting_cash=self.starting_cash, commission_multiplier=self.commission_multiplier, min_commission=self.min_commission, tax_multiplier=self.tax_multiplier) context = Context.get_instance() #stock account expect_market_value_stock = 0 expect_cash_stock = self.starting_cash["STOCK"] expect_total_value_stock = expect_cash_stock + expect_market_value_stock #future account expect_market_value_future = 0 expect_cash_future = self.starting_cash["FUTURE"] expect_total_value_future = expect_cash_future + expect_market_value_future self.expect_total_value_future = expect_total_value_future #portfolio expect_total_value = expect_total_value_stock + expect_total_value_future self.trading_dts = context.get_available_trading_dts() first_trading_dt = self.trading_dts[0] self.order_book_id = self.order_book_ids[0] print("-------------first_trading_dt: {}----------".format( context.trading_dt)) to_submit_orders = order_target_weights({self.order_book_id: 0.5}) order = to_submit_orders[0] expect_deal_price = self.data_source.get_last_price( order_book_id=self.order_book_id, dt=first_trading_dt) expect_deal_money = expect_deal_price * order.quantity expect_commission_fee = expect_deal_money * self.commission_rate * self.commission_multiplier expect_tax = 0 # no tax rate when buy expect_transaction_cost = expect_commission_fee + expect_tax #pdb.set_trace() state, reward, is_done, info = self.env.step(action=to_submit_orders) first_trade = context.tracker._trades[0] #pdb.set_trace() self.first_trade = first_trade self.assertEqual(first=first_trade["order_book_id"], second=self.order_book_id) self.assertEqual(first=first_trade["trading_datetime"], second=first_trading_dt.strftime("%Y-%m-%d %H:%M:%S")) self.assertEqual(first=first_trade["last_price"], second=expect_deal_price) self.assertEqual(first=first_trade["commission"], second=expect_commission_fee) self.assertEqual(first=first_trade["tax"], second=expect_tax) self.assertEqual(first=first_trade["transaction_cost"], second=expect_transaction_cost) # portfolio and accounts change after trading #stock account expect_market_value_stock = expect_deal_money expect_cash_stock = self.starting_cash[ "STOCK"] - expect_market_value_stock - expect_transaction_cost #502448.2768 #expect_total_value_stock = expect_cash_stock + expect_market_value_stock #future account #expect_market_value_future = 0 #expect_cash_future = self.starting_cash["FUTURE"] #expect_total_value_future = expect_cash_future + expect_market_value_future #portfolio #expect_total_value = expect_total_value_stock + expect_total_value_future # settlement on the next bar next_trading_dt = self.trading_dts[1] expect_settlement_price = self.data_source.get_last_price( order_book_id=self.order_book_id, dt=next_trading_dt) expect_market_value_stock_settlement = first_trade[ "last_quantity"] * expect_settlement_price self.expect_cash_stock_settlement = expect_cash_stock self.expect_total_value_stock_settlement = self.expect_cash_stock_settlement + expect_market_value_stock_settlement self.expect_total_value_future_settlement = expect_total_value_future self.expect_total_value_settlement = self.expect_total_value_stock_settlement + self.expect_total_value_future_settlement expect_reward = (self.expect_total_value_settlement - expect_total_value) / expect_total_value self.assertAlmostEqual(first=reward, second=expect_reward)
def step(self, action): reward, is_done, info = self._executor.send(action) #pdb.set_trace() next_state = Context.get_instance().history_bars() return next_state, reward, is_done, info
def _on_before_trading(self, _): trading_date = Context.get_instance().trading_dt.date() for position in self._iter_pos(): self._total_cash += position.before_trading(trading_date)
def test_order_target_value(self): feature_df, price_s = create_toy_feature(order_book_ids_number=3, feature_number=3, random_seed=111) data_source = DataSource(feature_df=feature_df, price_s=price_s) all_order_book_ids = data_source.get_available_order_book_ids() print(all_order_book_ids) STOCK_INIT_CASH = 1000000 env = TradingEnv(data_source=data_source, look_backward_window=2, mode="non-rl", starting_cash={"STOCK": STOCK_INIT_CASH}, commission_multiplier=0, min_commission=0, tax_multiplier=0) state = env.reset() order_book_id = "000001.XSHE" print( "-----------------------current trading dt: {}-----------------------------------" .format(Context.get_instance().trading_dt)) last_price = Context.get_instance().get_last_price(order_book_id) to_submit_orders1 = order_target_value(order_book_id, 500000) expect_quantity = int((500000 / last_price) / 100) * 100 assert expect_quantity == to_submit_orders1.quantity self.assertEqual(first=expect_quantity, second=to_submit_orders1.quantity) state, reward, is_done, info = env.step(action=[to_submit_orders1]) current_positions0 = get_position(all_order_book_ids[0], POSITION_DIRECTION.LONG) print( "-----------------------current trading dt: {}-----------------------------------" .format(Context.get_instance().trading_dt)) last_price = Context.get_instance().get_last_price(order_book_id) print(last_price) #current_positions2 = get_position(all_order_book_ids[0], POSITION_DIRECTION.LONG) to_submit_orders2 = order_target_value(order_book_id, 600000) gap_value = 600000 - current_positions0.market_value expect_quantity = int((gap_value / last_price) / 100) * 100 assert expect_quantity == to_submit_orders2.quantity self.assertEqual(first=expect_quantity, second=to_submit_orders2.quantity) state, reward, is_done, info = env.step(action=[to_submit_orders2]) current_positions2 = get_position(all_order_book_ids[0], POSITION_DIRECTION.LONG) print( "-----------------------current trading dt: {}-----------------------------------" .format(Context.get_instance().trading_dt)) last_price = Context.get_instance().get_last_price(order_book_id) print("price: {}".format(last_price)) # to_submit_orders3 = order_value(order_book_id, 100000) to_submit_orders3 = order_target_value(order_book_id, 100000) gap_value = 100000 - current_positions2.market_value expect_quantity = abs(int((gap_value / last_price) / 100) * 100) self.assertEqual(first=expect_quantity, second=to_submit_orders3.quantity) self.assertEqual(first=to_submit_orders3.side, second=SIDE.SELL) self.assertEqual(first=to_submit_orders3.position_effect, second=POSITION_EFFECT.CLOSE)
def prev_close(self): if not is_valid_price(self._prev_close): context = Context.get_instance() self._prev_close = context.data_source.get_prev_close(self._order_book_id, context.trading_dt) return self._prev_close
def _get_account_position_ins(order_book_id): account = Context.get_instance().portfolio.accounts[ DEFAULT_ACCOUNT_TYPE.STOCK] position = account.get_position(order_book_id, POSITION_DIRECTION.LONG) return account, position
def _open_orders(self): for order in Context.get_instance().broker.get_open_orders(self.order_book_id): if order.position_direction == self._direction: yield order
def _get_tax(self, order_book_id, side, cost_money): instrument_type = Context.get_instance().data_source.instrument_type( order_book_id) if instrument_type != 'CS': return 0 return cost_money * self.tax_rate * self.tax_multiplier if side == SIDE.SELL else 0
def order_target_weights(target_weights: Dict[str, float]) -> List[Order]: """ make the account position to touch the target position :param target_weights: a dictionary contain the target weight of position :example: .. code-block:: python # adjust positions, to make the '000001.XSHE' to touch the target percent of account 10% # make the '000002.XSHE' to touch the target percent of account 15% order_target_weights({ '000001.XSHE': 0.1 '000002.XSHE': 0.15 }) """ total_percent = sum(six.itervalues(target_weights)) if total_percent > 1 and not np.isclose(total_percent, 1): raise RuntimeError("total percent should be lower than 1, current: {}" ).format(total_percent) context = Context.get_instance() account = context.portfolio.accounts[DEFAULT_ACCOUNT_TYPE.STOCK] account_value = account.get_current_trading_dt_total_value() # target_quantities = {} for order_book_id, target_percent in target_weights.items(): if target_percent < 0: raise RuntimeError( "target percent of {} should between 0 and 1, current: {}". format(order_book_id, target_percent)) price = context.get_last_price(order_book_id) #print("trading_dt:{} current price: {}".format(context.trading_dt, price)) if not is_valid_price(price): print("Order Creation Failed: [{order_book_id}] No market data". format(order_book_id=order_book_id)) continue target_quantity = account_value * target_percent / price target_quantities[order_book_id] = int( round(target_quantity / 100) * 100) #target_quantity# close_orders, open_orders = [], [] current_quantities = { p.order_book_id: p.quantity for p in account.get_positions() if p.direction == POSITION_DIRECTION.LONG } for order_book_id, quantity in current_quantities.items(): if order_book_id not in target_weights: close_orders.append( Order.__from_create__(order_book_id, quantity, SIDE.SELL, MarketOrder(), POSITION_EFFECT.CLOSE)) round_lot = 100 for order_book_id, target_quantity in target_quantities.items(): if order_book_id in current_quantities: delta_quantity = target_quantity - current_quantities[order_book_id] else: delta_quantity = target_quantity if delta_quantity >= round_lot: delta_quantity = math.floor(delta_quantity / round_lot) * round_lot open_orders.append( Order.__from_create__(order_book_id, delta_quantity, SIDE.BUY, MarketOrder(), POSITION_EFFECT.OPEN)) elif delta_quantity < -1: delta_quantity = math.floor(delta_quantity) close_orders.append( Order.__from_create__(order_book_id, abs(delta_quantity), SIDE.SELL, MarketOrder(), POSITION_EFFECT.CLOSE)) to_submit_orders = [] for order in chain(close_orders, open_orders): #print("to submit order: {}".format(order)) to_submit_orders.append(order) return to_submit_orders
def _register_event(self): event_bus = Context.get_instance().event_bus event_bus.prepend_listener(EVENT.PRE_BEFORE_TRADING, self._pre_before_trading) event_bus.prepend_listener(EVENT.POST_SETTLEMENT, self._post_settlement)