mirror of
https://github.com/c9s/bbgo.git
synced 2024-11-25 08:15:15 +00:00
support json output for backtesting
This commit is contained in:
parent
1e151a170a
commit
474be4e815
|
@ -21,12 +21,13 @@ func (c *AverageCostCalculator) Calculate(symbol string, trades []types.Trade, c
|
||||||
|
|
||||||
if len(trades) == 0 {
|
if len(trades) == 0 {
|
||||||
return &AverageCostPnlReport{
|
return &AverageCostPnlReport{
|
||||||
Symbol: symbol,
|
Symbol: symbol,
|
||||||
CurrentPrice: currentPrice,
|
Market: c.Market,
|
||||||
NumTrades: 0,
|
LastPrice: currentPrice,
|
||||||
BuyVolume: bidVolume,
|
NumTrades: 0,
|
||||||
SellVolume: askVolume,
|
BuyVolume: bidVolume,
|
||||||
FeeInUSD: feeUSD,
|
SellVolume: askVolume,
|
||||||
|
FeeInUSD: feeUSD,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -68,10 +69,11 @@ func (c *AverageCostCalculator) Calculate(symbol string, trades []types.Trade, c
|
||||||
|
|
||||||
unrealizedProfit := (fixedpoint.NewFromFloat(currentPrice) - position.AverageCost).Mul(position.Base)
|
unrealizedProfit := (fixedpoint.NewFromFloat(currentPrice) - position.AverageCost).Mul(position.Base)
|
||||||
return &AverageCostPnlReport{
|
return &AverageCostPnlReport{
|
||||||
Symbol: symbol,
|
Symbol: symbol,
|
||||||
CurrentPrice: currentPrice,
|
Market: c.Market,
|
||||||
NumTrades: len(trades),
|
LastPrice: currentPrice,
|
||||||
StartTime: time.Time(trades[0].Time),
|
NumTrades: len(trades),
|
||||||
|
StartTime: time.Time(trades[0].Time),
|
||||||
|
|
||||||
BuyVolume: bidVolume,
|
BuyVolume: bidVolume,
|
||||||
SellVolume: askVolume,
|
SellVolume: askVolume,
|
||||||
|
@ -80,7 +82,7 @@ func (c *AverageCostCalculator) Calculate(symbol string, trades []types.Trade, c
|
||||||
Profit: totalProfit,
|
Profit: totalProfit,
|
||||||
NetProfit: totalNetProfit,
|
NetProfit: totalNetProfit,
|
||||||
UnrealizedProfit: unrealizedProfit,
|
UnrealizedProfit: unrealizedProfit,
|
||||||
AverageBidCost: position.AverageCost.Float64(),
|
AverageCost: position.AverageCost.Float64(),
|
||||||
FeeInUSD: (totalProfit - totalNetProfit).Float64(),
|
FeeInUSD: (totalProfit - totalNetProfit).Float64(),
|
||||||
CurrencyFees: currencyFees,
|
CurrencyFees: currencyFees,
|
||||||
}
|
}
|
||||||
|
|
|
@ -3,8 +3,11 @@ package cmd
|
||||||
import (
|
import (
|
||||||
"bufio"
|
"bufio"
|
||||||
"context"
|
"context"
|
||||||
|
"encoding/json"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"io/ioutil"
|
||||||
"os"
|
"os"
|
||||||
|
"path/filepath"
|
||||||
"strings"
|
"strings"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
|
@ -28,6 +31,7 @@ func init() {
|
||||||
BacktestCmd.Flags().CountP("verbose", "v", "verbose level")
|
BacktestCmd.Flags().CountP("verbose", "v", "verbose level")
|
||||||
BacktestCmd.Flags().String("config", "config/bbgo.yaml", "strategy config file")
|
BacktestCmd.Flags().String("config", "config/bbgo.yaml", "strategy config file")
|
||||||
BacktestCmd.Flags().Bool("force", false, "force execution without confirm")
|
BacktestCmd.Flags().Bool("force", false, "force execution without confirm")
|
||||||
|
BacktestCmd.Flags().String("output", "", "the report output directory")
|
||||||
RootCmd.AddCommand(BacktestCmd)
|
RootCmd.AddCommand(BacktestCmd)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -65,6 +69,12 @@ var BacktestCmd = &cobra.Command{
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
outputDirectory, err := cmd.Flags().GetString("output")
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
jsonOutputEnabled := len(outputDirectory) > 0
|
||||||
|
|
||||||
syncOnly, err := cmd.Flags().GetBool("sync-only")
|
syncOnly, err := cmd.Flags().GetBool("sync-only")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
|
@ -270,7 +280,7 @@ var BacktestCmd = &cobra.Command{
|
||||||
|
|
||||||
calculator := &pnl.AverageCostCalculator{
|
calculator := &pnl.AverageCostCalculator{
|
||||||
TradingFeeCurrency: backtestExchange.PlatformFeeCurrency(),
|
TradingFeeCurrency: backtestExchange.PlatformFeeCurrency(),
|
||||||
Market: market,
|
Market: market,
|
||||||
}
|
}
|
||||||
|
|
||||||
startPrice, ok := session.StartPrice(symbol)
|
startPrice, ok := session.StartPrice(symbol)
|
||||||
|
@ -278,14 +288,14 @@ var BacktestCmd = &cobra.Command{
|
||||||
return fmt.Errorf("start price not found: %s", symbol)
|
return fmt.Errorf("start price not found: %s", symbol)
|
||||||
}
|
}
|
||||||
|
|
||||||
log.Infof("%s PROFIT AND LOSS REPORT", symbol)
|
|
||||||
log.Infof("===============================================")
|
|
||||||
|
|
||||||
lastPrice, ok := session.LastPrice(symbol)
|
lastPrice, ok := session.LastPrice(symbol)
|
||||||
if !ok {
|
if !ok {
|
||||||
return fmt.Errorf("last price not found: %s", symbol)
|
return fmt.Errorf("last price not found: %s", symbol)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
log.Infof("%s PROFIT AND LOSS REPORT", symbol)
|
||||||
|
log.Infof("===============================================")
|
||||||
|
|
||||||
report := calculator.Calculate(symbol, trades.Trades, lastPrice)
|
report := calculator.Calculate(symbol, trades.Trades, lastPrice)
|
||||||
report.Print()
|
report.Print()
|
||||||
|
|
||||||
|
@ -298,6 +308,29 @@ var BacktestCmd = &cobra.Command{
|
||||||
log.Infof("FINAL BALANCES:")
|
log.Infof("FINAL BALANCES:")
|
||||||
finalBalances.Print()
|
finalBalances.Print()
|
||||||
|
|
||||||
|
if jsonOutputEnabled {
|
||||||
|
result := struct {
|
||||||
|
Symbol string `json:"symbol,omitempty"`
|
||||||
|
PnLReport *pnl.AverageCostPnlReport `json:"pnlReport,omitempty"`
|
||||||
|
InitialBalances types.BalanceMap `json:"initialBalances,omitempty"`
|
||||||
|
FinalBalances types.BalanceMap `json:"finalBalances,omitempty"`
|
||||||
|
}{
|
||||||
|
Symbol: symbol,
|
||||||
|
PnLReport: report,
|
||||||
|
InitialBalances: initBalances,
|
||||||
|
FinalBalances: finalBalances,
|
||||||
|
}
|
||||||
|
|
||||||
|
jsonOutput, err := json.MarshalIndent(&result,"", " ")
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := ioutil.WriteFile(filepath.Join(outputDirectory, symbol + ".json"), jsonOutput, 0644) ; err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if wantBaseAssetBaseline {
|
if wantBaseAssetBaseline {
|
||||||
initBaseAsset := inBaseAsset(initBalances, market, startPrice)
|
initBaseAsset := inBaseAsset(initBalances, market, startPrice)
|
||||||
finalBaseAsset := inBaseAsset(finalBalances, market, lastPrice)
|
finalBaseAsset := inBaseAsset(finalBalances, market, lastPrice)
|
||||||
|
|
|
@ -3,10 +3,11 @@ package types
|
||||||
import (
|
import (
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
"fmt"
|
"fmt"
|
||||||
"github.com/leekchan/accounting"
|
|
||||||
"math"
|
"math"
|
||||||
"strconv"
|
"strconv"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
|
"github.com/leekchan/accounting"
|
||||||
)
|
)
|
||||||
|
|
||||||
type Duration time.Duration
|
type Duration time.Duration
|
||||||
|
@ -48,27 +49,27 @@ func (d *Duration) UnmarshalJSON(data []byte) error {
|
||||||
}
|
}
|
||||||
|
|
||||||
type Market struct {
|
type Market struct {
|
||||||
Symbol string
|
Symbol string `json:"symbol"`
|
||||||
LocalSymbol string // LocalSymbol is used for exchange's API
|
LocalSymbol string `json:"localSymbol,omitempty" `// LocalSymbol is used for exchange's API
|
||||||
|
|
||||||
PricePrecision int
|
PricePrecision int `json:"pricePrecision,omitempty"`
|
||||||
VolumePrecision int
|
VolumePrecision int `json:"volumePrecision,omitempty"`
|
||||||
QuoteCurrency string
|
QuoteCurrency string `json:"quoteCurrency,omitempty"`
|
||||||
BaseCurrency string
|
BaseCurrency string `json:"baseCurrency,omitempty"`
|
||||||
|
|
||||||
// The MIN_NOTIONAL filter defines the minimum notional value allowed for an order on a symbol.
|
// The MIN_NOTIONAL filter defines the minimum notional value allowed for an order on a symbol.
|
||||||
// An order's notional value is the price * quantity
|
// An order's notional value is the price * quantity
|
||||||
MinNotional float64
|
MinNotional float64 `json:"minNotional,omitempty"`
|
||||||
MinAmount float64
|
MinAmount float64 `json:"minAmount,omitempty"`
|
||||||
|
|
||||||
// The LOT_SIZE filter defines the quantity
|
// The LOT_SIZE filter defines the quantity
|
||||||
MinQuantity float64
|
MinQuantity float64 `json:"minQuantity,omitempty"`
|
||||||
MaxQuantity float64
|
MaxQuantity float64 `json:"maxQuantity,omitempty"`
|
||||||
StepSize float64
|
StepSize float64 `json:"stepSize,omitempty"`
|
||||||
|
|
||||||
MinPrice float64
|
MinPrice float64 `json:"minPrice,omitempty"`
|
||||||
MaxPrice float64
|
MaxPrice float64 `json:"maxPrice,omitempty"`
|
||||||
TickSize float64
|
TickSize float64 `json:"tickSize,omitempty"`
|
||||||
}
|
}
|
||||||
|
|
||||||
func (m Market) BaseCurrencyFormatter() *accounting.Accounting {
|
func (m Market) BaseCurrencyFormatter() *accounting.Accounting {
|
||||||
|
|
Loading…
Reference in New Issue
Block a user