-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathcli.go
More file actions
183 lines (159 loc) · 6.45 KB
/
Copy pathcli.go
File metadata and controls
183 lines (159 loc) · 6.45 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
package aperiodic
import (
"flag"
"fmt"
"io"
"os"
)
type CLI struct {
Stdout io.Writer
Stderr io.Writer
Env func(string) string
}
func NewCLI() *CLI {
return &CLI{
Stdout: os.Stdout,
Stderr: os.Stderr,
Env: os.Getenv,
}
}
func (c *CLI) Run(args []string) int {
if len(args) == 0 {
c.printUsage()
return 0
}
cmd := args[0]
if cmd == "help" || cmd == "-h" || cmd == "--help" {
c.printUsage()
return 0
}
fs := flag.NewFlagSet("aperiodic", flag.ContinueOnError)
fs.SetOutput(c.Stderr)
exchangeFlag := fs.String("exchange", "binance-futures", "Exchange name")
symbolFlag := fs.String("symbol", "", "Trading pair symbol")
intervalFlag := fs.String("interval", "1h", "Aggregation interval")
startDateFlag := fs.String("start-date", "", "Start date (YYYY-MM-DD)")
endDateFlag := fs.String("end-date", "", "End date (YYYY-MM-DD)")
maxConcurrentFlag := fs.Int("max-concurrent", 10, "Maximum concurrent downloads")
timestampFlag := fs.String("timestamp", "exchange", "Timestamp source (exchange, true)")
outputDirFlag := fs.String("output-dir", "", "Output directory for Parquet files (mandatory)")
previewFlag := fs.Bool("preview", false, "Query the free preview dataset (no subscription; whitelisted parameters only)")
if err := fs.Parse(args[1:]); err != nil {
return 2
}
apiKey := c.Env("APERIODIC_API_KEY")
if apiKey == "" {
if *previewFlag {
// Preview data is served against the shared demo key, so no key is required.
apiKey = DemoAPIKey
} else {
fmt.Fprintln(c.Stderr, "Error: APERIODIC_API_KEY environment variable not set (pass --preview to use the shared demo key)")
return 1
}
}
client := NewAperiodicClient(apiKey)
if cmd == "symbols" {
return c.handleSymbols(client, *exchangeFlag)
}
if *outputDirFlag == "" {
fmt.Fprintln(c.Stderr, "Error: --output-dir is mandatory")
return 1
}
return c.handleData(client, cmd, *timestampFlag, *intervalFlag, *exchangeFlag, *symbolFlag, *startDateFlag, *endDateFlag, *maxConcurrentFlag, *outputDirFlag, *previewFlag)
}
func (c *CLI) printUsage() {
fmt.Fprintln(c.Stdout, "Aperiodic CLI Client")
fmt.Fprintln(c.Stdout)
fmt.Fprintln(c.Stdout, "Usage:")
fmt.Fprintln(c.Stdout, " aperiodic <metric> [flags]")
fmt.Fprintln(c.Stdout, " aperiodic symbols [flags]")
fmt.Fprintln(c.Stdout)
fmt.Fprintln(c.Stdout, "Metrics:")
fmt.Fprintln(c.Stdout, " ohlcv OHLCV (open/high/low/close/volume)")
fmt.Fprintln(c.Stdout, " vtwap Volume/time-weighted average price")
fmt.Fprintln(c.Stdout, " flow Buy/sell trade flow")
fmt.Fprintln(c.Stdout, " trade_size Trade size distribution")
fmt.Fprintln(c.Stdout, " impact Price impact")
fmt.Fprintln(c.Stdout, " range Price range")
fmt.Fprintln(c.Stdout, " updownticks Up/down tick count")
fmt.Fprintln(c.Stdout, " run_structure Run structure")
fmt.Fprintln(c.Stdout, " returns Returns")
fmt.Fprintln(c.Stdout, " slippage Slippage")
fmt.Fprintln(c.Stdout, " l1_price L1 best bid/ask price")
fmt.Fprintln(c.Stdout, " l1_imbalance L1 order book imbalance")
fmt.Fprintln(c.Stdout, " l1_liquidity L1 liquidity")
fmt.Fprintln(c.Stdout, " l2_imbalance L2 order book imbalance")
fmt.Fprintln(c.Stdout, " l2_liquidity L2 liquidity")
fmt.Fprintln(c.Stdout, " basis Basis (spot vs. perp spread)")
fmt.Fprintln(c.Stdout, " funding Funding rates")
fmt.Fprintln(c.Stdout, " open_interest Open interest")
fmt.Fprintln(c.Stdout, " derivative_price Derivative price")
fmt.Fprintln(c.Stdout)
fmt.Fprintln(c.Stdout, "Commands:")
fmt.Fprintln(c.Stdout, " symbols List available symbols for an exchange")
fmt.Fprintln(c.Stdout, " help Show this help")
fmt.Fprintln(c.Stdout)
fmt.Fprintln(c.Stdout, "Environment:")
fmt.Fprintln(c.Stdout, " APERIODIC_API_KEY Aperiodic API key (required, except with --preview)")
fmt.Fprintln(c.Stdout)
fmt.Fprintln(c.Stdout, "Flags:")
fmt.Fprintln(c.Stdout, " -end-date string")
fmt.Fprintln(c.Stdout, " End date (YYYY-MM-DD)")
fmt.Fprintln(c.Stdout, " -exchange string")
fmt.Fprintln(c.Stdout, " Exchange name (default \"binance-futures\")")
fmt.Fprintln(c.Stdout, " -interval string")
fmt.Fprintln(c.Stdout, " Aggregation interval (default \"1h\")")
fmt.Fprintln(c.Stdout, " -max-concurrent int")
fmt.Fprintln(c.Stdout, " Maximum concurrent downloads (default 10)")
fmt.Fprintln(c.Stdout, " -output-dir string")
fmt.Fprintln(c.Stdout, " Output directory for Parquet files (mandatory)")
fmt.Fprintln(c.Stdout, " -preview")
fmt.Fprintln(c.Stdout, " Query the free preview dataset (no subscription; whitelisted parameters only)")
fmt.Fprintln(c.Stdout, " -start-date string")
fmt.Fprintln(c.Stdout, " Start date (YYYY-MM-DD)")
fmt.Fprintln(c.Stdout, " -symbol string")
fmt.Fprintln(c.Stdout, " Trading pair symbol")
fmt.Fprintln(c.Stdout, " -timestamp string")
fmt.Fprintln(c.Stdout, " Timestamp source (exchange, true) (default \"exchange\")")
}
func (c *CLI) handleSymbols(client *AperiodicClient, exchange string) int {
symbols, err := client.GetSymbols(exchange)
if err != nil {
fmt.Fprintf(c.Stderr, "Error fetching symbols: %v\n", err)
return 1
}
for _, s := range symbols {
fmt.Fprintln(c.Stdout, s)
}
return 0
}
func (c *CLI) handleData(client *AperiodicClient, metric, timestamp, interval, exchange, symbol, startDate, endDate string, maxConcurrent int, outputDir string, preview bool) int {
if symbol == "" {
fmt.Fprintln(c.Stderr, "Error: --symbol is required")
return 1
}
if startDate == "" || endDate == "" {
fmt.Fprintln(c.Stderr, "Error: --start-date and --end-date are required")
return 1
}
resp, err := client.FetchPresignedUrls(metric, TimestampType(timestamp), Interval(interval), exchange, symbol, startDate, endDate, preview)
if err != nil {
fmt.Fprintf(c.Stderr, "Error fetching file URLs: %v\n", err)
return 1
}
if len(resp.Files) == 0 {
fmt.Fprintln(c.Stdout, "No data found for the given criteria")
return 0
}
fmt.Fprintf(c.Stdout, "Downloading %d Parquet files to %s...\n", len(resp.Files), outputDir)
results, err := client.DownloadFilesConcurrently(resp.Files, maxConcurrent, outputDir)
if err != nil {
fmt.Fprintf(c.Stderr, "Error downloading files: %v\n", err)
return 1
}
fmt.Fprintf(c.Stdout, "Successfully downloaded %d files:\n", len(results))
for _, res := range results {
fmt.Fprintf(c.Stdout, " - %s\n", res.Filename)
}
return 0
}