Skip to content

Commit

Permalink
testing/loadbot: Add moving markets.
Browse files Browse the repository at this point in the history
Add several tools to the loadbot to allow for sideways, trending, and
volatile markets. Remove most mining as continuous mining is provided by
the harnesses themselves, and this allows us to run multiple bots at
once without excessive mining.

To allow for sideways and trending markets, new options for the
sidestacker can be used. These are a flat increase to the rate or an
automatic oscillating effect. Volatile markets can be achieved by
running the ts bot separately that randomly buys, or sells, large sums
moving the market violently.
  • Loading branch information
JoeGruffins committed Jul 21, 2023
1 parent 8ed2bbf commit 0b474b4
Show file tree
Hide file tree
Showing 12 changed files with 522 additions and 170 deletions.
8 changes: 8 additions & 0 deletions dex/testing/dcrdex/harness.sh
Original file line number Diff line number Diff line change
Expand Up @@ -130,6 +130,14 @@ if [ $ETH_ON -eq 0 ]; then
"epochDuration": ${EPOCH_DURATION},
"marketBuyBuffer": 1.2
},
{
"base": "BTC_simnet",
"quote": "ETH_simnet",
"lotSize": 1000000,
"rateStep": 1000,
"epochDuration": ${EPOCH_DURATION},
"marketBuyBuffer": 1.2
},
{
"base": "DCR_simnet",
"quote": "DEXTT_simnet",
Expand Down
9 changes: 8 additions & 1 deletion dex/testing/dgb/harness.sh
Original file line number Diff line number Diff line change
Expand Up @@ -20,10 +20,17 @@ export DELTA_WALLET_SEED="ehA5EC6mbNvGCJ4HqtjNc822ojnU2bGRLoc3cZipoYFFYT7tYrq4"
export DELTA_ADDRESS="dgbrt1qdgzj8guegzuyfupcvy3vjlpfxpv8rgruqtkndf"
# Signal that the node needs to restart after encrypting wallet
export RESTART_AFTER_ENCRYPT="1"
export NOMINER="1"
# $1 is the node to create with. $2 is the wallet name
export NEW_WALLET_CMD="./\$1 createwallet \$2"
export BLOCKS_TO_MINE=120 # it chokes with high diff if you mine much more, but if you stop at 100, nothing much is mature it seems
export EXTRA_ARGS="-disabledandelion=1 -rpcbind=0.0.0.0 -rpcallowip=0.0.0.0/0 -nodiscover -nodnsseed" # -onlynet=ipv4 -nodiscover

# Background watch mining by default:
# 'export NOMINER="1"' or uncomment this line to disable
#
# TODO: Cannot presently continuously mine on simnet but when we can comment
# this out.
NOMINER="1"

# Run the harness
../btc/base-harness.sh
4 changes: 4 additions & 0 deletions dex/testing/doge/harness.sh
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,10 @@ SESSION="${SYMBOL}-harness"

SHELL=$(which bash)

# Background watch mining by default:
# 'export NOMINER="1"' or uncomment this line to disable
#NOMINER="1"

################################################################################
# Load prepared wallets.
################################################################################
Expand Down
9 changes: 8 additions & 1 deletion dex/testing/loadbot/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,8 @@ pingpong **traders** in parallel.
The **sidestacker** program runs 2 **traders**, one seller and one buyer. Each
epoch, the **traders** will attempt to create order book depth on their side.
If the book is deep enough already, they will place taker orders targeting
the other side.
the other side. Can be further altered by setting a linear increase
(trending market) or an automatic ascending/descending pattern (sideways market).

#### compound

Expand All @@ -38,6 +39,12 @@ The **heavy** program is like **compound** on steroids. Four
**sidestacker traders** with 6 orders per epoch, and a 5-order per epoch
**sniper**.

#### ts

The **ts** program runs multiple **traders** that randomly push the market in
one direction or the other. Can be run separately with other programs to create
a volatile market.

### Logging

Debug logging can be enabled with the `-debug` flag. Trace logging can be
Expand Down
30 changes: 20 additions & 10 deletions dex/testing/loadbot/compound.go
Original file line number Diff line number Diff line change
Expand Up @@ -13,17 +13,20 @@ import (
// runCompound runs the 'compound' program, consisting of 2 (5/3) unmetered
// sideStackers, a 1-order sniper, and a pingPonger.
func runCompound() {
go blockEvery2()

var oscillator uint64
var wg sync.WaitGroup
wg.Add(4)
go func() {
defer wg.Done()
runTrader(newSideStacker(true, 5, 3, alpha, false, log.SubLogger("STACKER:0")), "CMPD:STACKER:0")
seller, metered, oscillatorWrite := true, false, true
runTrader(newSideStacker(20, 3, alpha, seller, metered, oscillatorWrite,
&oscillator, log.SubLogger("STACKER:0")), "CMPD:STACKER:0")
}()
go func() {
defer wg.Done()
runTrader(newSideStacker(false, 5, 3, alpha, false, log.SubLogger("STACKER:1")), "CMPD:STACKER:1")
seller, metered, oscillatorWrite := false, false, false
runTrader(newSideStacker(20, 3, alpha, seller, metered, oscillatorWrite,
&oscillator, log.SubLogger("STACKER:1")), "CMPD:STACKER:1")
}()
go func() {
defer wg.Done()
Expand Down Expand Up @@ -59,25 +62,32 @@ func runHeavy() {
}
}

go blockEvery2()

var oscillator uint64
var wg sync.WaitGroup
wg.Add(5)
go func() {
defer wg.Done()
runTrader(newSideStacker(true, 12, 6, alpha, true, log.SubLogger("STACKER:0")), "HEAVY:STACKER:0")
seller, metered, oscillatorWrite := true, false, true
runTrader(newSideStacker(24, 6, alpha, seller, metered, oscillatorWrite,
&oscillator, log.SubLogger("STACKER:0")), "HEAVY:STACKER:0")
}()
go func() {
defer wg.Done()
runTrader(newSideStacker(false, 12, 6, alpha, true, log.SubLogger("STACKER:1")), "HEAVY:STACKER:1")
seller, metered, oscillatorWrite := false, false, false
runTrader(newSideStacker(24, 6, alpha, seller, metered, oscillatorWrite,
&oscillator, log.SubLogger("STACKER:1")), "HEAVY:STACKER:1")
}()
go func() {
defer wg.Done()
runTrader(newSideStacker(true, 8, 4, beta, false, log.SubLogger("STACKER:2")), "HEAVY:STACKER:2")
seller, metered, oscillatorWrite := true, false, false
runTrader(newSideStacker(16, 4, beta, seller, metered, oscillatorWrite,
&oscillator, log.SubLogger("STACKER:2")), "HEAVY:STACKER:2")
}()
go func() {
defer wg.Done()
runTrader(newSideStacker(false, 8, 4, beta, false, log.SubLogger("STACKER:3")), "HEAVY:STACKER:3")
seller, metered, oscillatorWrite := false, false, false
runTrader(newSideStacker(16, 4, beta, seller, metered, oscillatorWrite,
&oscillator, log.SubLogger("STACKER:3")), "HEAVY:STACKER:3")
}()
go func() {
defer wg.Done()
Expand Down
57 changes: 50 additions & 7 deletions dex/testing/loadbot/loadbot.go
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,12 @@ const (
dextt = "dextt.eth"
maxOrderLots = 10
ethFeeRate = 200 // gwei
// missedCancelErrStr is part of an error found in dcrdex/server/market/orderrouter.go
// that a cancel order may hit with bad timing but is not a problem.
//
// TODO: Consider returning a separate msgjson error from server for
// this case.
missedCancelErrStr = "target order not known:"
)

var (
Expand All @@ -87,7 +93,7 @@ var (

usr, _ = user.Current()
dextestDir = filepath.Join(usr.HomeDir, "dextest")
botDir = filepath.Join(dextestDir, "loadbot")
botDir = filepath.Join(dextestDir, fmt.Sprintf("loadbot_%d", time.Now().Unix()))
alphaIPCFile = filepath.Join(dextestDir, "eth", "alpha", "node", "geth.ipc")
betaIPCFile = filepath.Join(dextestDir, "eth", "beta", "node", "geth.ipc")

Expand All @@ -102,12 +108,14 @@ var (
epochDuration uint64
lotSize uint64
rateStep uint64
rateShift, rateIncrease int64
conversionFactors = make(map[string]uint64)

ethInitFee = (dexeth.InitGas(1, 0) + dexeth.RefundGas(0)) * ethFeeRate
ethRedeemFee = dexeth.RedeemGas(1, 0) * ethFeeRate
defaultMidGap, marketBuyBuffer float64
keepMidGap bool
ethInitFee = (dexeth.InitGas(1, 0) + dexeth.RefundGas(0)) * ethFeeRate
ethRedeemFee = dexeth.RedeemGas(1, 0) * ethFeeRate
defaultMidGap, marketBuyBuffer float64
keepMidGap, oscillate, randomOsc, ignoreErrors bool
oscInterval, oscStep, tsFrequency uint64

processesMtx sync.Mutex
processes []*process
Expand Down Expand Up @@ -352,6 +360,13 @@ func run() error {
flag.BoolVar(&trace, "trace", false, "use trace logging")
flag.IntVar(&m, "m", 0, "for compound and sidestacker, m is the number of makers to stack before placing takers")
flag.IntVar(&n, "n", 0, "for compound and sidestacker, n is the number of orders to place per epoch (default 3)")
flag.Int64Var(&rateShift, "rateshift", 0, "for compound and sidestacker, rateShift is applied to every order and increases or decreases price by the chosen shift times the rate step, use to create a market trending in one direction (default 0)")
flag.BoolVar(&oscillate, "oscillate", false, "for compound and sidestacker, whether the price should move up and down inside a window, use to emulate a sideways market (default false)")
flag.BoolVar(&randomOsc, "randomosc", false, "for compound and sidestacker, oscillate more randomly")
flag.Uint64Var(&oscInterval, "oscinterval", 300, "for compound and sidestacker, the number of epochs to take for a full oscillation cycle")
flag.Uint64Var(&oscStep, "oscstep", 50, "for compound and sidestacker, the number of rate step to increase or decrease per epoch")
flag.BoolVar(&ignoreErrors, "ignoreerrors", false, "log and ignore errors rather than the default behavior of stopping loadbot")
flag.Uint64Var(&tsFrequency, "tsfrequency", 4, "controls the frequency with which the ts \"whales\" after it is ready. To whale is to choose a rate and attempt to buy up the entire book at that price. If frequency is N, ts will whale an average of 1 out of every N+1 epochs (default 4)")
flag.Parse()

if programName == "" {
Expand Down Expand Up @@ -435,6 +450,9 @@ func run() error {
marketBuyBuffer = mkt.MBBuffer
break
}

rateIncrease = int64(rateStep) * rateShift

// Adjust to be comparable to the dcr_btc market.
defaultMidGap = defaultBtcPerDcr * float64(rateStep) / 100

Expand Down Expand Up @@ -464,7 +482,7 @@ func run() error {
if err != nil {
return fmt.Errorf("error creating LoggerMaker: %v", err)
}
log /* global */ = loggerMaker.NewLogger("LOADBOT", dex.LevelInfo)
log /* global */ = loggerMaker.NewLogger("LOADBOT")

log.Infof("Running program %s", programName)

Expand Down Expand Up @@ -534,6 +552,7 @@ func run() error {
if err != nil {
return fmt.Errorf("error creating loadbot directory: %v", err)
}
defer os.RemoveAll(botDir)

// Run any specified network conditions.
var toxics toxiproxy.Toxics
Expand Down Expand Up @@ -651,11 +670,13 @@ func run() error {
case "pingpong4":
runPingPong(4)
case "sidestacker":
runSideStacker(5, 3)
runSideStacker(20, 3)
case "compound":
runCompound()
case "heavy":
runHeavy()
case "ts":
runTS()
default:
log.Criticalf("program " + programName + " not known")
}
Expand Down Expand Up @@ -698,3 +719,25 @@ func loadNodeConfig(symbol, node string) map[string]string {
}
return cfg
}

func symmetricWalletConfig(numCoins int, midGap uint64) (
minBaseQty, maxBaseQty, minQuoteQty, maxQuoteQty uint64) {

minBaseQty = uint64(maxOrderLots) * uint64(numCoins) * lotSize
minQuoteQty = calc.BaseToQuote(midGap, minBaseQty)
// Ensure enough for registration fees.
minBaseQty += 2e8
minQuoteQty += 2e8
// eth fee estimation calls for more reserves.
// TODO: polygon and tokens
if quoteSymbol == eth {
add := (ethRedeemFee + ethInitFee) * uint64(maxOrderLots)
minQuoteQty += add
}
if baseSymbol == eth {
add := (ethRedeemFee + ethInitFee) * uint64(maxOrderLots)
minBaseQty += add
}
maxBaseQty, maxQuoteQty = minBaseQty*2, minQuoteQty*2
return
}

0 comments on commit 0b474b4

Please sign in to comment.