123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685 |
- package lmaxapi
- import "tickserver/api/lmaxapi/request"
- import "tickserver/api/lmaxapi/response"
- import "math"
- import "log"
- import "errors"
- type Account struct {
- positionList *PositionList
- orderList *OrderList
- state *response.AccountStateEvent
- detail *response.AccountDetails
- mtf *Mtf
- inId map[string]string
- clientId int64
- }
- func NewAccount(mtf *Mtf, positionList *PositionList, orderList *OrderList, clientId int64) *Account {
- account := &Account{}
- if orderList == nil {
- orderList = NewOrderList(mtf)
- }
- if positionList == nil {
- positionList = NewPositionList(mtf, orderList)
- }
- account.positionList = positionList
- account.orderList = orderList
- account.mtf = mtf
- account.clientId = clientId
- account.inId = make(map[string]string)
- return account
- }
- func (this *Account) GetId() int64 {
- return this.state.AccountId
- }
- func (this *Account) HasInId(id string) bool {
- if _, ok := this.inId[id]; ok {
- return true
- }
- return false
- }
- var ErrFindServerInId = errors.New("ErrFindServerInId")
- func (this *Account) GetInId(id string) (string, error) {
- if serverId, ok := this.inId[id]; ok {
- return serverId, nil
- }
- return "", ErrFindServerInId
- }
- func (this *Account) DelInId(id string) {
- delete(this.inId, id)
- }
- func (this *Account) SetInId(id string, serverId string) {
- this.inId[id] = serverId
- }
- func (this *Account) SetState(account *response.AccountStateEvent) *response.Instrument {
- this.state = account
- // log.Println("[Account][SetState]", account)
- return nil
- }
- func (this *Account) SetDetail(account *response.AccountDetails) *response.Instrument {
- this.detail = account
- // log.Println("[Account][SetDetail]", account)
- return nil
- }
- func (this *Account) SetRejected(event *response.InstructionRejectedEvent) {
- }
- func (this *Account) GetState() *response.AccountStateEvent {
- if this.state == nil {
- return nil
- }
- account := *this.state
- return &account
- }
- type TickEvent struct {
- ob2 *response.OrderBookEvent
- fetchUpdated bool
- isCopy bool
- }
- func NewTickEvent(ob2 *response.OrderBookEvent, fetchUpdated bool, isCopy bool) *TickEvent {
- req := &TickEvent{}
- req.ob2 = ob2
- req.fetchUpdated = fetchUpdated
- req.isCopy = isCopy
- return req
- }
- func (event *TickEvent) GetOb2() *response.OrderBookEvent {
- return event.ob2
- }
- type AccountUpdated struct {
- Req interface{}
- orders []*response.OrderEvent
- positions []*response.PositionEvent
- account *response.AccountStateEvent
- }
- func (this *AccountUpdated) GetOrders() []*response.OrderEvent {
- return this.orders
- }
- func (this *AccountUpdated) GetPositions() []*response.PositionEvent {
- return this.positions
- }
- func (this *AccountUpdated) GetAccount() *response.AccountStateEvent {
- return this.account
- }
- func (this *Account) UpdateTick(event *TickEvent) *AccountUpdated {
- up := &AccountUpdated{}
- up.positions = this.positionList.UpdateTick(event)
- up.orders = this.orderList.UpdateTick(event)
- if len(up.positions) > 0 || len(up.orders) > 0 {
- this.updateAccount()
- up.account = this.GetState()
- }
- return up
- }
- func (this *Account) SetPosition(event *response.PositionEvent) {
- this.positionList.SetPosition(event)
- this.updateAccount()
- }
- func (this *Account) SetOrder(event *response.OrderEvent) {
- this.orderList.SetOrder(event)
- }
- func (this *Account) UpdateOrder(event *response.OrderEvent) *AccountUpdated {
- orders := this.orderList.UpdateOrder(event)
- update := &AccountUpdated{}
- update.orders = orders
- //更新position 并更新wallet
- prev := this.positionList.GetPosition(event.InstrumentId)
- update.positions = append(update.positions, this.positionList.updateByOrder(event.InstrumentId))
- current := update.positions[0]
- inst := this.mtf.getInstument(event.InstrumentId)
- if inst == nil {
- return nil
- }
- if inst.AssetClass == "SPOT" { //如果是现货
- if current.LongUnfilledQuantity > 0 { //buy
- wallet := this.getWallet(inst.Currency)
- wallet.Freeze = current.LongUnfilledCost
- wallet.Balance += prev.OpenCost - current.OpenCost
- this.setWallet(wallet)
- } else {
- wallet := this.getWallet(inst.UnitOfMeasure)
- wallet.Freeze = -current.ShortUnfilledQuantity
- wallet.Balance += prev.OpenQuantity - current.OpenQuantity
- this.setWallet(wallet)
- }
- }
- this.updateAccount()
- update.account = this.state
- return update
- }
- func (this *Account) SetExecution(event *response.ExecutionEvent) {
- this.updateByExecution(positionChange{}, &event.OrderEvent)
- this.updateAccount()
- }
- //Execution
- func (this *Account) updateByExecution(pchange positionChange, event *response.OrderEvent) {
- if len(event.Executions) == 0 {
- return
- }
- inst := this.mtf.getInstument(event.InstrumentId)
- if inst == nil {
- return
- }
- //开仓:
- free := this.calcCommission(event)
- if pchange.quantity == 0 || sameSign(pchange.quantity, event.Executions[0].Quantity) {
- this.updateWalletItem(inst.Currency, -free)
- log.Println("open order Commission:", free)
- } else { //平仓的话,要包括 费用 + 盈利
- closeCost := 0.0
- totalq := 0.0
- for i := 0; i < len(event.Executions); i++ {
- quantity := event.Executions[i].Quantity
- price := event.Executions[i].Price
- dq := quantity
- if math.Abs(pchange.quantity)-math.Abs(totalq+quantity) < 0 {
- dq = -(pchange.quantity + totalq)
- }
- closeCost += dq * price * inst.ContractSize
- totalq += dq
- if math.Abs(pchange.quantity+totalq) < 0.000001 {
- break
- }
- }
- realisedpl := -(closeCost + (-totalq)*pchange.cost/pchange.quantity)
- log.Println("close order info:", closeCost, pchange.cost, realisedpl, free)
- this.updateWalletItem(inst.Currency, realisedpl-free)
- }
- }
- func (this *Account) updateWalletItem(currency string, money float64) {
- wallet := this.state.Wallets
- for i := 0; i < len(wallet); i++ {
- if wallet[i].Currency == currency {
- wallet[i].Balance += money
- break
- }
- }
- }
- type RateEvent struct {
- erate *response.ExchangeRateEvent
- fetchUpdated bool
- isCopy bool
- }
- func NewRateEvent(erate *response.ExchangeRateEvent, fetchUpdated bool, isCopy bool) *RateEvent {
- req := &RateEvent{}
- req.erate = erate
- req.isCopy = isCopy
- req.fetchUpdated = fetchUpdated
- return req
- }
- type AccountInfo struct {
- ob2 []*response.OrderBookEvent
- erate []*response.ExchangeRateEvent
- accountState *response.AccountStateEvent
- orders *response.Orders
- positions *response.Positions
- }
- func (this *AccountInfo) GetAllOb2() []*response.OrderBookEvent {
- return this.ob2
- }
- func (this *AccountInfo) GetAllRate() []*response.ExchangeRateEvent {
- return this.erate
- }
- func (this *AccountInfo) GetAccount() *response.AccountStateEvent {
- return this.accountState
- }
- func (this *AccountInfo) GetOrders() *response.Orders {
- return this.orders
- }
- func (this *AccountInfo) GetPositions() *response.Positions {
- return this.positions
- }
- func (event *RateEvent) GetRate() *response.ExchangeRateEvent {
- return event.erate
- }
- var ErrInstrumentNotFound = errors.New("ErrInstrumentNotFound")
- var ErrNoMargin = errors.New("ErrNoMargin")
- func (this *Account) CheckMargin(msg *Message) error {
- if msg.Type == MsgCancelOrder || msg.Type == MsgAmendOrder || msg.Type == MsgCloseOrder {
- return nil
- }
- if msg.Type == MsgPlaceOrder {
- req := msg.Data.(*request.OrderRequest)
- inst := this.mtf.getInstument(req.InstrumentId)
- if inst == nil {
- return ErrInstrumentNotFound
- }
- if inst.AssetClass == "SPOT" { //现货购买,直接判断balance 是否足够
- if req.Quantity > 0 { //buy
- wallet := this.getWallet(inst.Currency)
- money := wallet.Balance
- if req.Quantity*req.Price < money {
- return ErrNoMargin
- }
- } else { // sell
- wallet := this.getWallet(inst.UnitOfMeasure)
- money := wallet.Balance
- if req.Quantity+money < 0 {
- return ErrNoMargin
- }
- }
- }
- }
- return nil
- }
- //获取钱包信息
- func (this *Account) getWallet(currency string) response.Wallet {
- wallet := this.state.Wallets
- for i := 0; i < len(wallet); i++ {
- if wallet[i].Currency == currency {
- return wallet[i]
- }
- }
- w := response.Wallet{}
- w.Currency = currency
- return w
- }
- func (this *Account) setWallet(w response.Wallet) {
- wallet := this.state.Wallets
- var isset bool
- for i := 0; i < len(wallet); i++ {
- if wallet[i].Currency == w.Currency {
- wallet[i] = w
- isset = true
- }
- }
- if !isset {
- this.state.Wallets = append(this.state.Wallets, w)
- }
- if w.Currency == this.detail.Currency {
- this.state.Balance = w.Balance
- this.state.Freeze = w.Freeze
- }
- }
- func (this *Account) UpdateRate(event *RateEvent) *AccountUpdated {
- up := &AccountUpdated{}
- up.positions = this.positionList.UpdateRate(event)
- up.orders = this.orderList.UpdateRate(event)
- if len(up.positions) > 0 || len(up.orders) > 0 || this.hasCurrency(event.erate.From) {
- this.updateAccount()
- up.account = this.GetState()
- }
- return up
- }
- func (this *Account) hasCurrency(currency string) bool {
- for i := 0; i < len(this.state.Wallets); i++ {
- if this.state.Wallets[i].Currency == currency {
- return true
- }
- }
- return false
- }
- type walletInfo struct {
- Currency string
- Balance float64
- Profit float64
- Margin float64
- exchangeRate *response.ExchangeRateEvent
- }
- type workingMargin struct {
- buy float64
- sell float64
- }
- func (this *Account) noPositionWorkingOrderMargin(info map[string]*walletInfo) {
- margins := make(map[int64]*workingMargin)
- for i := 0; i < len(this.orderList.data); i++ {
- o := this.orderList.data[i]
- if _, ok := this.positionList.perSymbol[o.InstrumentId]; ok {
- continue
- }
- if o.IsWorking() {
- if _, ok := margins[o.InstrumentId]; !ok {
- margins[o.InstrumentId] = &workingMargin{}
- }
- if o.Quantity > 0 {
- margins[o.InstrumentId].buy += o.Margin
- } else {
- margins[o.InstrumentId].sell += o.Margin
- }
- }
- }
- for key, item := range margins {
- w := this.initWalletInfo(key, info)
- if w == nil {
- continue
- }
- if item.buy > item.sell {
- w.Margin += item.buy
- } else {
- w.Margin += item.sell
- }
- }
- }
- func (this *Account) initWalletInfo(instId int64, info map[string]*walletInfo) *walletInfo {
- inst, ok := this.mtf.instrument[instId]
- if !ok {
- return nil
- }
- symbol := inst.Currency
- w, ok := info[symbol]
- if !ok {
- w = &walletInfo{}
- info[symbol] = w
- w.Currency = symbol
- w.exchangeRate = this.mtf.getExchangeRate(symbol)
- }
- return w
- }
- //更新钱包,每天五点开始更新
- func (this *Account) updateWallets() {
- balance := 0.0
- wallet := this.state.Wallets
- for i := 0; i < len(wallet); i++ {
- if wallet[i].Currency == this.detail.Currency {
- balance += wallet[i].Balance
- continue
- }
- if data, ok := this.mtf.erate[wallet[i].Currency+"/"+this.detail.Currency]; ok {
- money := doRateExchange(wallet[i].Balance, data)
- data.Wallet = 0
- //send a log event
- //mtf.server.SendPrivate()
- balance += money
- }
- }
- w := response.Wallet{}
- w.Currency = this.detail.Currency
- w.Balance = balance
- this.state.Wallets = nil
- this.state.Wallets = append(this.state.Wallets, w)
- }
- //利息计算,每天的五点进行,计算方法见trade manal
- func (this *Account) updateSwap() {
- //计算position的Open量,然后进行处理
- for _, position := range this.positionList.perSymbol {
- inst := this.mtf.getInstument(position.InstrumentId)
- if position.OpenQuantity == 0 {
- continue
- }
- money := 0.0
- if position.OpenQuantity > 0 {
- money = position.OpenQuantity * inst.LongSwapPoints
- } else {
- money = position.OpenQuantity * inst.ShortSwapPoints
- }
- if inst.Currency != this.detail.Currency {
- if data, ok := this.mtf.erate[inst.Currency+"/"+this.detail.Currency]; ok {
- money = doRateExchange2(money, data)
- } else {
- log.Println("updateSwap::no exchange rate")
- continue
- }
- }
- //更新钱包:
- wallet := this.state.Wallets
- for i := 0; i < len(wallet); i++ {
- if wallet[i].Currency == inst.Currency {
- wallet[i].Balance += money
- break
- }
- }
- }
- }
- //佣金计算
- //1. 如果订单是分裂的,如何计算?关键是这个问题
- //2. 这个函数只计算当前订单增加的佣金,历史的佣金情况我们要自己累积
- func (this *Account) calcCommission(order *response.OrderEvent) float64 {
- inst := this.mtf.getInstument(order.InstrumentId)
- if inst == nil {
- return 0.0
- }
- ret := float64(0)
- for i := 0; i < len(order.Executions); i++ {
- dt := inst.AggressiveCommissionRate * order.Executions[i].Price * order.Executions[i].Quantity * inst.ContractSize
- if dt < 0 {
- dt = -dt
- }
- ret += dt / float64(10000)
- }
- return ret
- }
- var moneyPrec = 4
- func (this *Account) updateAccount() {
- if this.state == nil {
- return
- }
- balance := 0.0
- wallet := this.state.Wallets
- info := make(map[string]*walletInfo)
- for i := 0; i < len(wallet); i++ {
- symbol := wallet[i].Currency
- w := &walletInfo{}
- info[symbol] = w
- w.Currency = symbol
- w.Balance = wallet[i].Balance
- // log.Println(wallet[i])
- // log.Println(this.detail)
- if wallet[i].Currency == this.detail.Currency {
- balance += wallet[i].Balance
- continue
- }
- if data, ok := this.mtf.erate[wallet[i].Currency+"/"+this.detail.Currency]; ok {
- data.Wallet = wallet[i].Balance
- balance += doRateExchange(data.Wallet, data)
- w.exchangeRate = data
- }
- }
- for _, position := range this.positionList.perSymbol {
- w := this.initWalletInfo(position.InstrumentId, info)
- if w == nil {
- continue
- }
- w.Profit += position.Profit
- w.Margin += position.Margin
- }
- this.noPositionWorkingOrderMargin(info)
- equity := 0.0
- availableFunds := 0.0
- //做汇总
- for _, w := range info {
- equity += doRateExchange(w.Balance+w.Profit, w.exchangeRate)
- availableFunds += doRateExchange(w.Balance+w.Profit-w.Margin, w.exchangeRate)
- }
- balance = round(balance, moneyPrec)
- equity = round(equity, moneyPrec)
- availableFunds = round(availableFunds, moneyPrec)
- profit := round(equity-balance, moneyPrec)
- margin := round(equity-availableFunds, moneyPrec)
- this.state.UnrealisedProfitAndLoss = profit
- this.state.Margin = margin
- this.state.AvailableFunds = availableFunds
- this.state.AvailableToWithdraw = availableFunds
- this.state.Balance = balance
- this.state.Equity = equity
- }
- func round(val float64, prec int) float64 {
- var rounder float64
- n := math.Pow(10, float64(prec))
- intermed := val * n
- delta := intermed - math.Floor(intermed)
- if delta >= 0.5 {
- rounder = math.Ceil(intermed)
- } else {
- rounder = math.Floor(intermed)
- }
- return rounder / n
- }
- func doRateExchange(money float64, exchangeRate *response.ExchangeRateEvent) float64 {
- if exchangeRate != nil {
- if money > 0 {
- money *= exchangeRate.Buy
- } else {
- money *= exchangeRate.Sell
- }
- return money
- }
- return money
- }
- func doRateExchange2(money float64, exchangeRate *response.ExchangeRateEvent) float64 {
- if exchangeRate != nil {
- if money > 0 {
- money /= exchangeRate.Buy
- } else {
- money /= exchangeRate.Sell
- }
- return money
- }
- return money
- }
- //在交易模式下面,初始化在一次运行只初始化一次。
- //在仓单管理模式下面,初始化会初始化多次。(比如断线后会重置)
- //订单的更新:
- //在tick变化, exchangerate变化的情况下更新
- //在某次执行的时候更新
- func (this *Account) Init(event *InitEvent) *AccountUpdated {
- // log.Println("[Account][Init]", this.detail)
- //删除已经不存在的order 和 position
- updated := &AccountUpdated{}
- mpositon := make(map[int64]*response.PositionEvent)
- for i := 0; i < len(event.Position); i++ {
- mpositon[event.Position[i].InstrumentId] = event.Position[i]
- }
- morder := make(map[string]*response.OrderEvent)
- for i := 0; i < len(event.Order); i++ {
- morder[event.Order[i].OrderId] = event.Order[i]
- }
- //订单不存在,设置成平仓
- for i := 0; i < len(this.orderList.data); i++ {
- o := this.orderList.data[i]
- if _, ok := morder[o.OrderId]; !ok {
- o.SetClosed()
- tmp := *o
- updated.orders = append(updated.orders, &tmp)
- }
- }
- //会发送消息给客户端端,这个函数
- this.orderList.deleteClosed()
- for _, position := range this.positionList.perSymbol {
- if _, ok := mpositon[position.InstrumentId]; !ok {
- position.OpenQuantity = 0
- tmp := *position
- updated.positions = append(updated.positions, &tmp)
- }
- }
- this.positionList.deleteClosed()
- for i := 0; i < len(event.Order); i++ {
- this.SetOrder(event.Order[i])
- }
- for i := 0; i < len(event.Position); i++ {
- this.positionList.SetPosition(event.Position[i])
- }
- this.updateAccount()
- updated.account = this.GetState()
- return updated
- }
- type positionChange struct {
- quantity float64
- cost float64
- }
- func (this *Account) SetOneExecution(event *OneExecutionEvent) *AccountUpdated {
- updated := &AccountUpdated{}
- pchange := make(map[int64]*positionChange)
- for i := 0; i < len(event.Order); i++ {
- instrumentId := event.Order[i].InstrumentId
- if _, ok := pchange[instrumentId]; !ok {
- pchange[instrumentId] = &positionChange{}
- prevp, ok := this.positionList.perSymbol[instrumentId]
- if !ok {
- pchange[instrumentId].quantity = 0
- pchange[instrumentId].cost = 0
- } else {
- pchange[instrumentId].quantity = prevp.OpenQuantity
- pchange[instrumentId].cost = prevp.OpenCost
- }
- }
- updated.orders = append(updated.orders, event.Order[i])
- if event.Order[i].IsStopProft() {
- continue
- }
- this.orderList.SetOrder(event.Order[i])
- position := this.positionList.updateByOrder(instrumentId)
- if position != nil {
- updated.positions = append(updated.positions, position)
- }
- }
- for i := 0; i < len(event.Order); i++ {
- if event.Order[i].IsExecution() {
- //主要是在close的情况下,更新钱包的信息
- instrumentId := event.Order[i].InstrumentId
- this.updateByExecution(*pchange[instrumentId], event.Order[i])
- }
- }
- this.positionList.deleteClosed()
- this.updateAccount()
- updated.account = this.GetState()
- return updated
- }
- func sameSign(a float64, b float64) bool {
- if a == b {
- return true
- }
- if a > 0 && b > 0 {
- return true
- }
- if a < 0 && b < 0 {
- return true
- }
- return false
- }
|