Skip to content

Commit

Permalink
feat: portfolio functionality with tests & db implementation (#1)
Browse files Browse the repository at this point in the history
* feat: portfolio functionality with tests & db implementation

* feat: loading, checking, and saving bot config

* wip: fix tests and add server
  • Loading branch information
AnishP15 authored Aug 18, 2023
1 parent 380309c commit 1403520
Show file tree
Hide file tree
Showing 23 changed files with 837 additions and 178 deletions.
4 changes: 4 additions & 0 deletions .env.bot
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
MNEMONIC:
CHAIN_ID: nibiru-localnet-0
GRPC_ENDPOINT: localhost:9090
TMRPC_ENDPOINT: http://localhost:26657
Binary file removed bot.db
Binary file not shown.
4 changes: 4 additions & 0 deletions bot/.env.bot
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
MNEMONIC="region funny client second stone glow someone box fortune plunge file oyster panel slot galaxy tank rich dynamic orbit electric error rocket report odor"
CHAIN_ID="nibiru-localnet-0"
GRPC_ENDPOINT="localhost:9090"
TMRPC_ENDPOINT="http://localhost:26657"
Binary file added bot/bot.db
Binary file not shown.
168 changes: 86 additions & 82 deletions bot.go → bot/bot.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,6 @@ import (
"log"
"os"

"cosmossdk.io/math"
"github.com/NibiruChain/nibiru/app"
"github.com/NibiruChain/nibiru/x/common"
"github.com/NibiruChain/nibiru/x/common/asset"
Expand All @@ -17,16 +16,15 @@ import (
"github.com/cosmos/cosmos-sdk/crypto/keyring"
sdk "github.com/cosmos/cosmos-sdk/types"
"github.com/joho/godotenv"
"google.golang.org/grpc"
)

var _ = app.BankModule.Name

type BotState struct {
Positions map[string]PositionFields
Amms map[string]AmmFields
Prices map[string]Prices
Funds map[string]sdk.Coins
Positions map[string]PositionFields
Amms map[string]AmmFields
Prices map[string]Prices
PortfolioBalances Portfolio
}

type PositionFields struct {
Expand Down Expand Up @@ -71,13 +69,6 @@ const (
DontTrade
)

func main() {
var LOGGING_FILE = "test-log.txt"
SetupLoggingFile(LOGGING_FILE)
fmt.Print(LOGGING_FILE)

}

func LoadBot() (*Bot, error) {
godotenv.Load()
var GRPC_ENDPOINT = os.Getenv("GRPC_ENDPONT")
Expand All @@ -95,16 +86,9 @@ func LoadBot() (*Bot, error) {
TMRPC_ENDPOINT = gonibi.DefaultNetworkInfo.TmRpcEndpoint
}

grpcClientConnection, err := gonibi.GetGRPCConnection(
GRPC_ENDPOINT, true, 5)

if err != nil {
return nil, err
}

return NewBot(BotArgs{
ChainId: CHAIN_ID,
GrpcConn: grpcClientConnection,
GrpcEndpt: GRPC_ENDPOINT,
RpcEndpt: TMRPC_ENDPOINT,
Mnemonic: os.Getenv("VALIDATOR_MNEMONIC"),
UseMnemonic: true,
Expand All @@ -117,6 +101,9 @@ func Run(bot *Bot) error {

context := context.Background()

if err := bot.SyncState(); err != nil {
return err
}
// Querying info for Prices/Amms structs
err := bot.FetchNewPrices(context)
if err != nil {
Expand All @@ -129,6 +116,7 @@ func Run(bot *Bot) error {
} else {
bot.DB.PopulateAmmsTable(bot.State.Amms, blockHeight)
bot.DB.PopulatePricesTable(bot.State.Prices, blockHeight)
bot.State.PortfolioBalances.BlockNumber = blockHeight
}

//Querying trader address to find positions by
Expand All @@ -138,32 +126,68 @@ func Run(bot *Bot) error {
return fmt.Errorf("Cannot QueryAddress(): %s", err)
}

err = bot.FetchBalances(context)

balancesResp, err := bot.State.PortfolioBalances.Balances.QueryWalletCoins(
context, sdkAddress, bot.Gosdk.GrpcClient,
)
if err != nil {
return fmt.Errorf("Cannot FetchBalances: %s", err)
} else {
bot.DB.PopulateBalancesTable(bot.State.Funds, blockHeight)
return fmt.Errorf("Cannot QueryWalletCoins(): %s", err)
}

bot.State.PortfolioBalances.Balances.PopWalletCoins(balancesResp)

bot.DB.PopulateBalancesTable(bot.State.PortfolioBalances.Balances.WalletCoins,
sdkAddress.String(), blockHeight)

quoteToMove, err := bot.QuoteNeededToMovePrice()

if err != nil {
return fmt.Errorf("Cannot FindQuoteToMove: %s", err)
}

for pair, quoteAmount := range quoteToMove {
_, err := bot.PerformTradeAction(pair, quoteAmount, sdkAddress, context)
for pair, quote := range quoteToMove {
quoteAmount := quote.RoundInt()
_, action, err := bot.PerformTradeAction(pair, quoteAmount, sdkAddress, context)
if err != nil {
log.Fatalf("Cannot PerformTradeAction(): %v", err)
}

bot.UpdateTradeBalance(action, pair, quoteAmount)
}

balancesResp, err = bot.State.PortfolioBalances.Balances.QueryWalletCoins(
context, sdkAddress, bot.Gosdk.GrpcClient,
)
if err != nil {
return fmt.Errorf("Cannot QueryWalletCoins(): %s", err)
}

bot.State.PortfolioBalances.Balances.PopWalletCoins(balancesResp)

return nil
}

func (bot *Bot) PerformTradeAction(pair string, quoteAmount sdk.Dec,
trader sdk.AccAddress, ctx context.Context) (*sdk.TxResponse, error) {
func (bot *Bot) SyncState() (err error) {

// TODO: Query & Update Balances, Query & Update Wallets

return err
}

func (bot *Bot) UpdateTradeBalance(action TradeAction, pair string, quoteAmount sdk.Int) {

switch action {
case OpenOrder:
bot.State.PortfolioBalances.Balances.AddTradedBalances(pair,
sdk.NewCoin(asset.Pair(pair).QuoteDenom(), quoteAmount.Abs()))
case CloseOrder:
bot.State.PortfolioBalances.Balances.RemoveTradedBalances(pair,
sdk.NewCoin(asset.Pair(pair).QuoteDenom(), quoteAmount.Abs()))
}

}

func (bot *Bot) PerformTradeAction(pair string, quoteAmount sdk.Int,
trader sdk.AccAddress, ctx context.Context) (*sdk.TxResponse, TradeAction, error) {
_, posExists := bot.State.Positions[pair]

currPosition := CurrPosStats{
Expand All @@ -183,15 +207,18 @@ func (bot *Bot) PerformTradeAction(pair string, quoteAmount sdk.Dec,
action := EvaluateTradeAction(quoteAmount, bot.State.Amms[pair].Markets, posExists, currPosition)
switch action {
case OpenOrder:
return bot.OpenPosition(trader, quoteAmount.RoundInt(), sdk.NewDec(1), pair, ctx)
txResp, err := bot.OpenPosition(trader, quoteAmount, sdk.NewDec(1), pair, ctx)
return txResp, action, err
case CloseOrder:
return bot.ClosePosition(trader, pair, ctx)
txResp, err := bot.ClosePosition(trader, pair, ctx)
return txResp, action, err
case CloseAndOpenOrder:
return bot.CloseAndOpenPosition(trader, quoteAmount.RoundInt(), pair, ctx)
txResp, err := bot.CloseAndOpenPosition(trader, quoteAmount, pair, ctx)
return txResp, action, err
case DontTrade:
return nil, nil
return nil, action, nil
default:
return nil, fmt.Errorf("Invalid action type: %v", action)
return nil, action, fmt.Errorf("Invalid action type: %v", action)
}

}
Expand Down Expand Up @@ -219,8 +246,9 @@ func (bot *Bot) PopulateCurrPosStats(pair string) CurrPosStats {
return position
}

func EvaluateTradeAction(QuoteToMovePrice sdk.Dec, amm perpTypes.AMM, posExists bool, position CurrPosStats) TradeAction {
func EvaluateTradeAction(QuoteToMove sdk.Int, amm perpTypes.AMM, posExists bool, position CurrPosStats) TradeAction {

QuoteToMovePrice := sdk.NewDecFromInt(QuoteToMove)
if ShouldNotTrade(QuoteToMovePrice, amm.QuoteReserve) &&
posExists && position.IsAgainstMarket &&
position.MarketDelta.GT(position.CurrIndexPrice.Quo(sdk.NewDec(10))) {
Expand Down Expand Up @@ -259,7 +287,7 @@ func (bot *Bot) PopulateGosdkFromNetinfo(netinfo gonibi.NetworkInfo) *Bot {

type BotArgs struct {
ChainId string
GrpcConn *grpc.ClientConn
GrpcEndpt string
RpcEndpt string
Mnemonic string
UseMnemonic bool
Expand All @@ -270,7 +298,13 @@ const KEY_NAME = "bot"

func NewBot(args BotArgs) (*Bot, error) {

gosdk, err := gonibi.NewNibiruClient(args.ChainId, args.GrpcConn,
grpcConn, err := gonibi.GetGRPCConnection(args.GrpcEndpt, true, 5)

if err != nil {
return nil, err
}

gosdk, err := gonibi.NewNibiruClient(args.ChainId, grpcConn,
args.RpcEndpt)
if err != nil {
return nil, err
Expand Down Expand Up @@ -303,23 +337,23 @@ func NewBot(args BotArgs) (*Bot, error) {

return &Bot{
State: BotState{
Positions: make(map[string]PositionFields),
Amms: make(map[string]AmmFields),
Prices: make(map[string]Prices),
Funds: make(map[string]sdk.Coins),
Positions: make(map[string]PositionFields),
Amms: make(map[string]AmmFields),
Prices: make(map[string]Prices),
PortfolioBalances: *InitializePortfolio(),
},
Gosdk: &gosdk,
TmrpcAddr: args.RpcEndpt,
DB: CreateAndConnectDB(),
DB: CreateAndConnectDB("bot.db"),
KeyName: keyName,
}, nil
}

func (bot *Bot) OpenPosition(trader sdk.AccAddress, quoteToMove math.Int,
func (bot *Bot) OpenPosition(trader sdk.AccAddress, quoteToMove sdk.Int,
leverage sdk.Dec, pair string, ctx context.Context) (*sdk.TxResponse, error) {

var side int32 = 0
if quoteToMove.GT(math.NewInt(0)) {
if quoteToMove.GT(sdk.NewInt(0)) {
side = 1
} else {
side = 2
Expand All @@ -334,10 +368,8 @@ func (bot *Bot) OpenPosition(trader sdk.AccAddress, quoteToMove math.Int,
BaseAssetAmountLimit: sdk.NewInt(0),
})

fmt.Println("RESP: ", resp)

if err != nil {
log.Fatal("Cannot OpenPosition: ", err)
return nil, err
}
bot.FetchAndPopPositionsDB(trader, ctx)

Expand All @@ -346,7 +378,7 @@ func (bot *Bot) OpenPosition(trader sdk.AccAddress, quoteToMove math.Int,
}

func (bot *Bot) CloseAndOpenPosition(trader sdk.AccAddress,
quoteToMove math.Int, pair string, ctx context.Context) (*sdk.TxResponse, error) {
quoteToMove sdk.Int, pair string, ctx context.Context) (*sdk.TxResponse, error) {

_, openErr := bot.ClosePosition(trader, pair, ctx)

Expand Down Expand Up @@ -388,9 +420,9 @@ func (bot *Bot) FetchAndPopPositionsDB(trader sdk.AccAddress, ctx context.Contex
height, err := bot.GetBlockHeight(ctx, bot.TmrpcAddr)
if err != nil {
return err
} else {
bot.DB.PopulatePositionTable(bot.State.Positions, height)
}
bot.DB.PopulatePositionTable(bot.State.Positions, height)

return nil
}

Expand Down Expand Up @@ -420,27 +452,6 @@ func (bot *Bot) GetAddress() (sdk.AccAddress, error) {
return record.GetAddress()
}

// type Portofolio struct {
// splits (proportion of each balance)
// balances map[string]sdk.Int
// howMuchInEachPair map[string]sdk.Int
// }
// if trade sizes are small enough, no need to think about allocation

// token balance query
func (bot *Bot) FetchBalances(ctx context.Context) error {
moduleAccounts, err := bot.Gosdk.Querier.Perp.ModuleAccounts(ctx, &perpTypes.QueryModuleAccountsRequest{})

// create a map
if err != nil {
return err
}

bot.PopulateBalances(moduleAccounts)

return nil
}

func (bot *Bot) FetchPositions(trader string, ctx context.Context) error {

positions, err := bot.Gosdk.Querier.Perp.QueryPositions(ctx, &perpTypes.QueryPositionsRequest{
Expand Down Expand Up @@ -498,13 +509,6 @@ func (bot *Bot) PopulateAmms(queryMarketsResp *perpTypes.QueryMarketsResponse) {

}

func (bot *Bot) PopulateBalances(moduleAccounts *perpTypes.QueryModuleAccountsResponse) {

for _, perpAccount := range moduleAccounts.GetAccounts() {
bot.State.Funds[perpAccount.Address] = perpAccount.Balance
}
}

func (bot *Bot) PopulatePrices(oracle *oracleTypes.QueryExchangeRatesResponse,
queryMarkets *perpTypes.QueryMarketsResponse) {

Expand Down Expand Up @@ -645,9 +649,9 @@ func (bot *Bot) MakeZeroPosition(trader sdk.AccAddress, pair string, ctx context
Sender: trader.String(),
Pair: asset.Pair(pair),
Side: 2,
QuoteAssetAmount: math.ZeroInt(),
Leverage: math.LegacyZeroDec(),
BaseAssetAmountLimit: math.ZeroInt(),
QuoteAssetAmount: sdk.ZeroInt(),
Leverage: sdk.ZeroDec(),
BaseAssetAmountLimit: sdk.ZeroInt(),
})

}
Loading

0 comments on commit 1403520

Please sign in to comment.