step1. 客户端报单请求
客户端进行报单是通过ReqOrderInsert接口进行的,需要对入口参数:结构体类型CThostFtdcInputOrderField的30个字段进行填充。
数据被写入到FtdcPacketBuffer里,然后写入到PacketFlow<Packet> *ThostFtdcConnection::m_api_flow里
<BrokerID value="8000" />
<InvestorID value="000226" />
<InstrumentID value="cu1403" />
<OrderRef value="654321" />
<UserID value="8000_amdin" />
<OrderPriceType value="2" />
<Direction value="1" />
<CombOffsetFlag value="0" />
<CombHedgeFlag value="1" />
<LimitPrice value="69830" />
<VolumeTotalOriginal value="1" />
<TimeCondition value="3" />
<GTDDate value=" " />
<VolumeCondition value="1" />
<MinVolume value="1" />
<ContingentCondition value="1" />
<StopPrice value=" " />
<ForceCloseReason value="0" />
<IsAutoSuspend value="0" />
<BusinessUnit value="0249c2c" />
<RequestID value="2147483647" />
<UserForceClose value=" " />
<IsSwapOrder value=" " />
<ExchangeID value="SHFE" />
<AccountID value="" />
<CurrencyID value=" " />
<ClientID value=" " />
<InvestUnitID value="554444" />
<IPAddress value="1212" />
<MacAddress value="dqd" />
step2. 交易前置接收报单请求,处理并排队
交易前置的FtdcTraderHandler收到FtdcPacket后,设置order_insert::Parameters的这3个字段:
FrontID(来自g_front_id)
SessionID(来自connection_id)
MeasureNo
其余30个字段来自FTDInputOrderField(FTDInputOrderField又来自从客户端接收的FtdcPacket),共计33个字段,然后进行排队。
step3. 交易引擎进行基础业务处理
交易引擎从排队流水中读取SdtpPacket,根据order_insert::PROCEDURE_ID执行相应的基础业务处理函数order_insert,在order_insert内根据ExchangeID执行相应交易所版本的insert_order函数。下面以大商所的insert_order为例继续进行介绍。
1)数据同步状态检查,并设置TradingDay、SettlementID
2)会话检查
非超级会话需要校验是否在本中心具有交易权限
3)判断是否抢单
如果报单引用非空,则需根据FrontID+SessionID+OrderRef查找原来的报单,若找到原来的报单就是抢单,若没有找到,则继续报单。下面假设非抢单,继续进行介绍。
4)检查报单字段
5)设置报单状态OrderStatus、报单提交状态OrderSubmitStatus
6)交易权限检查
如果是期货或者期货组合则查找“大商所投资者合约交易权限”表,如果是期货期权则查找“大商所投资者期权交易权限”表,查找得到的交易权限不是“不能交易”就可以继续进行报单了。
7)设置报单的报单类型OrderType
8)取交易编码ClientID
9)条件单处理
10)handle_order
11)设置经纪公司报单编号BrokerOrderSeq
12)查找交易所报盘,并设置OrderInsertDate、OrderInsertTime、VolumeTotal、OrderLocalID
13)下单频率限制
14)插入“大商所报单表”
15)发送插入交易所报单命令
16)插入“大商所交易所报单表”
其中第10步handle_order是最为重要的一步:资金/持仓的冻结,以handle_single_order为例进行介绍
(1)find_position,查找持仓
确定持仓方向(净持仓、多头、空头)和持仓日期后,查找大商所投资者持仓表,找到了原来的持仓那就拿出来返回(新持仓同旧持仓),找不到原来的持仓就初始化新持仓(旧持仓为默认持仓)。
(2)change_frozen_position,调整持仓冻结
根据新报单的报单状态和报单提交状态计算未成交数量,并以该未成交数量为变化量,按照新报单的买卖方向分别增加新持仓的多头冻结LongFrozen或空头冻结ShortFrozen,最后结合新持仓的持仓(多空)方向判断是否为平仓。
注意:这里是插入报单期insert_order间进行的handle_single_order而且假定非抢单,所以没有旧报单,但是在别的场景下,比如交易所报单回报handle_exchange_order期间也会进行handle_single_order,彼时原始报单或者说旧报单是存在的,因此新持仓的多头冻结LongFrozen或空头冻结ShortFrozen的增加量将不再是新报单的未成交数量,而是新报单与原始报单的未成交数量的差值。由此可见:
差值为正即新报单未成交数量>原始报单未成交数量的情况下,多头冻结或者空头冻结是增加的;//这应该就是传说中的冻结,或者说增加冻结吧,显然报单插入时是肯定增加冻结的!
差值为0即新报单未成交数量=原始报单未成交数量的情况下,多头冻结或者空头冻结是不变的;
差值为负即新报单未成交数量<原始报单未成交数量的情况下,多头冻结或者空头冻结是减少的;//这应该就是传说中的解冻,或者说减少冻结吧,显然撤单插入时是肯定减少冻结的!
( 3)frozen_algorithm,调整资金冻结
a. get_order_price,获取冻结价格:
b. cal_fronzen_amount,计算新持仓的冻结资金LongFrozenAmount(或ShortFrozenAmount):冻结价格3000×(新多头冻结数量5-旧多头冻结数量0)×合约数量乘数10=150000
c. cal_frozen_margin,按合约的产品类型(期货或者期权)计算持仓的冻结保证金:根据指定经纪公司帐号查找“经纪公司保证金算法”表,然后根据找到的保证金算法的保证金价格类型(假设是昨结算价)获得保证金价格(根据钱提供的流水到这一步保证金价格由3000变成了0,因为昨结算价是0) ,接着查找【投资者保证金率】和【交易所保证金率】以计算冻结的保证金、保证金率、保证金率(按手数),最后判断新持仓的持仓多空方向,按照方向分别以不同的值来更新新持仓的冻结的保证金FrozenMargin、保证金率MarginRateByMoney、保证金率(按手数)MarginRateByVolume。
d. cal_cash,期货不需要计算新持仓的CashIn
e. cal_frozen_commission,计算新持仓的冻结手续费金额FrozenCommisision,
( 4)update_position,更新“大商所投资者持仓”表
---End---