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 }