summaryrefslogtreecommitdiff
path: root/golang
diff options
context:
space:
mode:
Diffstat (limited to '')
-rw-r--r--golang/pkg/agent/contracting.go54
-rw-r--r--golang/pkg/agent/trading.go139
-rw-r--r--golang/pkg/agent/utils.go18
-rw-r--r--golang/pkg/agent/visit.go12
-rw-r--r--golang/pkg/api/contracts.go53
-rw-r--r--golang/pkg/api/ships.go68
-rw-r--r--golang/pkg/api/systems.go50
-rw-r--r--golang/pkg/database/markets.go42
-rw-r--r--golang/pkg/database/sql/001_trading.sql2
-rw-r--r--golang/pkg/model/contract.go2
-rw-r--r--golang/pkg/model/market.go10
-rw-r--r--golang/pkg/model/trade_good.go11
12 files changed, 422 insertions, 39 deletions
diff --git a/golang/pkg/agent/contracting.go b/golang/pkg/agent/contracting.go
index 6d67d6d..ecd896a 100644
--- a/golang/pkg/agent/contracting.go
+++ b/golang/pkg/agent/contracting.go
@@ -15,7 +15,7 @@ func (a *agent) autoContracting(ship *model.Ship) {
return
}
for _, contract := range contracts {
- if contract.Fullfilled {
+ if contract.Fulfilled {
continue
}
now := time.Now()
@@ -27,11 +27,17 @@ func (a *agent) autoContracting(ship *model.Ship) {
}
}
a.channel <- fmt.Errorf("negotiating new contracts is not implemented yet")
- // TODO
- //for {
- // negotiate
- // runContract
- //}
+ for {
+ contract, err := a.client.NegotiateContract(ship)
+ if err != nil {
+ a.channel <- fmt.Errorf("failed to negotiate contract: %w", err)
+ return
+ }
+ if err := a.runContract(contract, ship); err != nil {
+ a.channel <- fmt.Errorf("failed to run contract %s: %w", contract.Id, err)
+ return
+ }
+ }
}
func (a *agent) runContract(contract *model.Contract, ship *model.Ship) error {
@@ -51,15 +57,33 @@ func (a *agent) runContract(contract *model.Contract, ship *model.Ship) error {
}
func (a *agent) runProcurement(contract *model.Contract, ship *model.Ship) error {
- deliveryCargo := contract.Terms.Deliver[0].TradeSymbol
- deliveryWaypoint, err := a.client.GetWaypoint(contract.Terms.Deliver[0].DestinationSymbol, a.db)
- if err != nil {
- return fmt.Errorf("failed to get delivery waypoint: %w", err)
+ if contract.Fulfilled {
+ return nil
+ }
+ deliver := contract.Terms.Deliver[0]
+ // make sure we are not carrying useless stuff
+ if err := a.sellEverythingExcept(ship, deliver.TradeSymbol); err != nil {
+ return fmt.Errorf("failed to sell everything except %s for ship %s: %w", deliver.TradeSymbol, ship.Symbol, err)
}
- for !contract.Fullfilled {
- _ = deliveryCargo
- _ = deliveryWaypoint
- return fmt.Errorf("not implemented")
+ // procure the desired goods
+ if ship.Cargo.Units < min(deliver.UnitsRequired-deliver.UnitsFulfilled, ship.Cargo.Capacity) {
+ if err := a.buyTradeGood(ship, deliver.TradeSymbol); err != nil {
+ return fmt.Errorf("failed to buy trade good %s with ship %s: %w", deliver.TradeSymbol, ship.Symbol, err)
+ }
+ }
+ // deliver the goods
+ if err := a.client.Navigate(ship, deliver.DestinationSymbol, a.db); err != nil {
+ return fmt.Errorf("failed to navigate to %s: %w", deliver.DestinationSymbol, err)
+ }
+ if err := a.client.Deliver(contract, ship, a.db); err != nil {
+ return fmt.Errorf("failed to deliver: %w", err)
+ }
+ deliver = contract.Terms.Deliver[0]
+ if deliver.UnitsRequired == deliver.UnitsFulfilled {
+ if err := a.client.Fulfill(contract, a.db); err != nil {
+ return fmt.Errorf("failed to fulfill: %w", err)
+ }
+ return nil
}
- return fmt.Errorf("not implemented")
+ return a.runProcurement(contract, ship)
}
diff --git a/golang/pkg/agent/trading.go b/golang/pkg/agent/trading.go
new file mode 100644
index 0000000..e84cef4
--- /dev/null
+++ b/golang/pkg/agent/trading.go
@@ -0,0 +1,139 @@
+package agent
+
+import (
+ "fmt"
+ "slices"
+
+ "git.adyxax.org/adyxax/spacetraders/golang/pkg/model"
+)
+
+type TradeGoodNotFoundError struct{}
+
+func (err *TradeGoodNotFoundError) Error() string {
+ return "trade good not found"
+}
+
+func (a *agent) buyTradeGood(ship *model.Ship, tradeGoodToBuy string) error {
+ if ship.Cargo.Units == ship.Cargo.Capacity {
+ return nil
+ }
+ // list markets would sell our goods
+ markets, err := a.listMarketsInSystem(ship.Nav.SystemSymbol)
+ if err != nil {
+ return fmt.Errorf("failed to list markets in system %s: %w", ship.Nav.SystemSymbol, err)
+ }
+ markets = slices.DeleteFunc(markets, func(market model.Market) bool {
+ for _, item := range market.Exports {
+ if item.Symbol == tradeGoodToBuy {
+ return false
+ }
+ }
+ return true
+ })
+ if len(markets) == 0 {
+ return &TradeGoodNotFoundError{}
+ }
+ // find the closest place to buy TODO
+ waypoint, err := a.client.GetWaypoint(ship.Nav.WaypointSymbol, a.db)
+ if err != nil {
+ return fmt.Errorf("failed to get nav waypoint %s: %w", ship.Nav.WaypointSymbol, err)
+ }
+ waypoints := make([]model.Waypoint, 0)
+ for i := range markets {
+ waypoint, err := a.client.GetWaypoint(markets[i].Symbol, a.db)
+ if err != nil {
+ return fmt.Errorf("failed to get waypoint %s: %w", markets[i].Symbol, err)
+ }
+ waypoints = append(waypoints, *waypoint)
+ }
+ sortByDistanceFrom(waypoint, waypoints)
+ // Go there and refresh our market data
+ if err := a.client.Navigate(ship, waypoints[0].Symbol, a.db); err != nil {
+ return fmt.Errorf("failed to navigate to %s: %w", waypoints[0].Symbol, err)
+ }
+ market, err := a.client.GetMarket(waypoints[0].Symbol, a.db)
+ if err != nil {
+ return fmt.Errorf("failed to get market %s: %w", waypoints[0].Symbol, err)
+ }
+ // Buy until full
+ for _, tradeGood := range market.TradeGoods {
+ if tradeGood.Type == "EXPORT" && tradeGood.Symbol == tradeGoodToBuy {
+ for ship.Cargo.Units < ship.Cargo.Capacity {
+ increment := min(ship.Cargo.Capacity-ship.Cargo.Units, tradeGood.TradeVolume)
+ if err := a.client.Purchase(ship, tradeGoodToBuy, increment, a.db); err != nil {
+ return fmt.Errorf("failed to purchase %d units of %s: %w", increment, tradeGoodToBuy, err)
+ }
+ }
+ break
+ }
+ }
+ return a.buyTradeGood(ship, tradeGoodToBuy)
+}
+
+func (a *agent) sellEverythingExcept(ship *model.Ship, keep string) error {
+ // First lets see what we need to sell
+ cargo := ship.Cargo.Inventory
+ cargo = slices.DeleteFunc(cargo, func(inventory model.Inventory) bool {
+ return inventory.Symbol == keep
+ })
+ if len(cargo) == 0 {
+ return nil
+ }
+ // list markets would buy our goods
+ markets, err := a.listMarketsInSystem(ship.Nav.SystemSymbol)
+ if err != nil {
+ return fmt.Errorf("failed to list markets in system %s: %w", ship.Nav.SystemSymbol, err)
+ }
+ markets = slices.DeleteFunc(markets, func(market model.Market) bool {
+ for _, item := range market.Imports {
+ for _, cargoItem := range cargo {
+ if item.Symbol == cargoItem.Symbol {
+ return false
+ }
+ }
+ }
+ return true
+ })
+ if len(markets) == 0 {
+ return nil
+ }
+ // find the closest place to sell something TODO
+ waypoint, err := a.client.GetWaypoint(ship.Nav.WaypointSymbol, a.db)
+ if err != nil {
+ return fmt.Errorf("failed to get nav waypoint %s: %w", ship.Nav.WaypointSymbol, err)
+ }
+ waypoints := make([]model.Waypoint, 0)
+ for i := range markets {
+ waypoint, err := a.client.GetWaypoint(markets[i].Symbol, a.db)
+ if err != nil {
+ return fmt.Errorf("failed to get waypoint %s: %w", markets[i].Symbol, err)
+ }
+ waypoints = append(waypoints, *waypoint)
+ }
+ sortByDistanceFrom(waypoint, waypoints)
+ // Go there and refresh our market data
+ if err := a.client.Navigate(ship, waypoints[0].Symbol, a.db); err != nil {
+ return fmt.Errorf("failed to navigate to %s: %w", waypoints[0].Symbol, err)
+ }
+ market, err := a.client.GetMarket(waypoints[0].Symbol, a.db)
+ if err != nil {
+ return fmt.Errorf("failed to get market %s: %w", waypoints[0].Symbol, err)
+ }
+ // sell everything we can
+ for _, cargoItem := range cargo {
+ units := cargoItem.Units
+ for _, tradeGood := range market.TradeGoods {
+ if tradeGood.Type == "IMPORT" && tradeGood.Symbol == cargoItem.Symbol {
+ for units > 0 {
+ increment := min(units, tradeGood.TradeVolume)
+ if err := a.client.Sell(ship, cargoItem.Symbol, increment, a.db); err != nil {
+ return fmt.Errorf("failed to sell %d units of %s: %w", units, cargoItem.Symbol, err)
+ }
+ units = units - increment
+ }
+ break
+ }
+ }
+ }
+ return a.sellEverythingExcept(ship, keep)
+}
diff --git a/golang/pkg/agent/utils.go b/golang/pkg/agent/utils.go
index 274e973..b274f43 100644
--- a/golang/pkg/agent/utils.go
+++ b/golang/pkg/agent/utils.go
@@ -40,6 +40,22 @@ func (a *agent) listWaypointsInSystemWithTrait(systemSymbol string, trait string
return waypoints, nil
}
+func (a *agent) listMarketsInSystem(systemSymbol string) ([]model.Market, error) {
+ waypoints, err := a.listWaypointsInSystemWithTrait(systemSymbol, "MARKETPLACE")
+ if err != nil {
+ return nil, fmt.Errorf("failed to list waypoints in system %s with trait MARKETPLACE: %w", systemSymbol, err)
+ }
+ var markets []model.Market
+ for i := range waypoints {
+ market, err := a.client.GetMarket(waypoints[i].Symbol, a.db)
+ if err != nil {
+ return nil, fmt.Errorf("failed to get market %s: %w", waypoints[i].Symbol, err)
+ }
+ markets = append(markets, *market)
+ }
+ return markets, nil
+}
+
func (a *agent) listShipyardsInSystem(systemSymbol string) ([]model.Shipyard, error) {
waypoints, err := a.listWaypointsInSystemWithTrait(systemSymbol, "SHIPYARD")
if err != nil {
@@ -47,7 +63,7 @@ func (a *agent) listShipyardsInSystem(systemSymbol string) ([]model.Shipyard, er
}
var shipyards []model.Shipyard
for i := range waypoints {
- shipyard, err := a.client.GetShipyard(&waypoints[i], a.db)
+ shipyard, err := a.client.GetShipyard(waypoints[i].Symbol, a.db)
if err != nil {
return nil, fmt.Errorf("failed to get shipyard %s: %w", waypoints[i].Symbol, err)
}
diff --git a/golang/pkg/agent/visit.go b/golang/pkg/agent/visit.go
index 4d79fef..3004553 100644
--- a/golang/pkg/agent/visit.go
+++ b/golang/pkg/agent/visit.go
@@ -39,9 +39,17 @@ func (a *agent) visitAllShipyards(ship *model.Ship) error {
if err := a.client.Navigate(ship, waypoints[0].Symbol, a.db); err != nil {
return fmt.Errorf("failed to navigate to %s: %w", waypoints[0].Symbol, err)
}
- if _, err := a.client.GetShipyard(&waypoints[0], a.db); err != nil {
+ if _, err := a.client.GetShipyard(waypoints[0].Symbol, a.db); err != nil {
return fmt.Errorf("failed to get shipyard %s: %w", waypoints[0].Symbol, err)
}
- // TODO get market data
+ // If this waypoint is also a marketplace, get its data
+ for _, trait := range waypoints[0].Traits {
+ if trait.Symbol == "MARKETPLACE" {
+ if _, err := a.client.GetMarket(waypoints[0].Symbol, a.db); err != nil {
+ return fmt.Errorf("failed to get market %s: %w", waypoints[0].Symbol, err)
+ }
+ break
+ }
+ }
return a.visitAllShipyards(ship)
}
diff --git a/golang/pkg/api/contracts.go b/golang/pkg/api/contracts.go
index 573d938..6a9a28a 100644
--- a/golang/pkg/api/contracts.go
+++ b/golang/pkg/api/contracts.go
@@ -20,16 +20,65 @@ func (c *Client) Accept(contract *model.Contract, db *database.DB) error {
}
var response acceptResponse
if err := c.Send("POST", &uriRef, nil, &response); err != nil {
- return fmt.Errorf("failed to accept contract %s: %w", contract.Id, err)
+ return fmt.Errorf("failed API request: %w", err)
}
if err := db.SaveAgent(response.Agent); err != nil {
- return fmt.Errorf("failed to accept contract %s: %w", contract.Id, err)
+ return fmt.Errorf("failed to save agent: %w", err)
}
contract.Accepted = response.Contract.Accepted
contract.Terms = response.Contract.Terms
return nil
}
+func (c *Client) Deliver(contract *model.Contract, ship *model.Ship, db *database.DB) error {
+ deliver := contract.Terms.Deliver[0]
+ var units int
+ for _, cargoItem := range ship.Cargo.Inventory {
+ if cargoItem.Symbol == deliver.TradeSymbol {
+ units = min(deliver.UnitsRequired-deliver.UnitsFulfilled, cargoItem.Units)
+ break
+ }
+ }
+ uriRef := url.URL{Path: path.Join("my/contracts", contract.Id, "deliver")}
+ type deliverRequest struct {
+ ShipSymbol string `json:"shipSymbol"`
+ TradeSymbol string `json:"tradeSymbol"`
+ Units int `json:"units"`
+ }
+ type deliverResponse struct {
+ Cargo *model.Cargo `json:"cargo"`
+ Contract *model.Contract `json:"contract"`
+ }
+ var response deliverResponse
+ if err := c.Send("POST", &uriRef, deliverRequest{ship.Symbol, deliver.TradeSymbol, units}, &response); err != nil {
+ return fmt.Errorf("failed API request: %w", err)
+ }
+ ship.Cargo = response.Cargo
+ contract.Terms = response.Contract.Terms
+ return nil
+}
+
+func (c *Client) Fulfill(contract *model.Contract, db *database.DB) error {
+ if contract.Fulfilled {
+ return nil
+ }
+ uriRef := url.URL{Path: path.Join("my/contracts", contract.Id, "fulfill")}
+ type fulfillResponse struct {
+ Agent *model.Agent `json:"agent"`
+ Contract *model.Contract `json:"contract"`
+ }
+ var response fulfillResponse
+ if err := c.Send("POST", &uriRef, nil, &response); err != nil {
+ return fmt.Errorf("failed API request: %w", err)
+ }
+ if err := db.SaveAgent(response.Agent); err != nil {
+ return fmt.Errorf("failed to save agent: %w", err)
+ }
+ contract.Fulfilled = response.Contract.Fulfilled
+ contract.Terms = response.Contract.Terms
+ return nil
+}
+
func (c *Client) MyContracts() ([]model.Contract, error) {
uriRef := url.URL{Path: "my/contracts"}
var contracts []model.Contract
diff --git a/golang/pkg/api/ships.go b/golang/pkg/api/ships.go
index 67a1ebd..21676d1 100644
--- a/golang/pkg/api/ships.go
+++ b/golang/pkg/api/ships.go
@@ -68,6 +68,18 @@ func (c *Client) Navigate(s *model.Ship, waypointSymbol string, db *database.DB)
return nil
}
+func (c *Client) NegotiateContract(s *model.Ship) (*model.Contract, error) {
+ uriRef := url.URL{Path: path.Join("my/ships", s.Symbol, "negotiate", "contract")}
+ type negotiateResponse struct {
+ Contract *model.Contract `json:"contract"`
+ }
+ var response negotiateResponse
+ if err := c.Send("POST", &uriRef, nil, &response); err != nil {
+ return nil, fmt.Errorf("failed API request: %w", err)
+ }
+ return response.Contract, nil
+}
+
func (c *Client) orbit(s *model.Ship) error {
if s.Nav.Status == "IN_ORBIT" {
return nil
@@ -84,6 +96,34 @@ func (c *Client) orbit(s *model.Ship) error {
return nil
}
+func (c *Client) Purchase(s *model.Ship, cargoItem string, units int, db *database.DB) error {
+ if err := c.dock(s); err != nil {
+ return fmt.Errorf("failed to dock: %w", err)
+ }
+ uriRef := url.URL{Path: path.Join("my/ships", s.Symbol, "purchase")}
+ type purchaseRequest struct {
+ Symbol string `json:"symbol"`
+ Units int `json:"units"`
+ }
+ type purchaseResponse struct {
+ Agent *model.Agent `json:"agent"`
+ Cargo *model.Cargo `json:"cargo"`
+ Transaction *model.Transaction `json:"transaction"`
+ }
+ var response purchaseResponse
+ if err := c.Send("POST", &uriRef, purchaseRequest{cargoItem, units}, &response); err != nil {
+ return fmt.Errorf("failed API request: %w", err)
+ }
+ if err := db.SaveAgent(response.Agent); err != nil {
+ return fmt.Errorf("failed to save agent: %w", err)
+ }
+ s.Cargo = response.Cargo
+ if err := db.AppendTransaction(response.Transaction); err != nil {
+ return fmt.Errorf("failed to append transaction: %w", err)
+ }
+ return nil
+}
+
func (c *Client) refuel(s *model.Ship, db *database.DB) error {
if s.Fuel.Current == s.Fuel.Capacity {
return nil
@@ -110,3 +150,31 @@ func (c *Client) refuel(s *model.Ship, db *database.DB) error {
}
return nil
}
+
+func (c *Client) Sell(s *model.Ship, cargoItem string, units int, db *database.DB) error {
+ if err := c.dock(s); err != nil {
+ return fmt.Errorf("failed to dock: %w", err)
+ }
+ uriRef := url.URL{Path: path.Join("my/ships", s.Symbol, "sell")}
+ type sellRequest struct {
+ Symbol string `json:"symbol"`
+ Units int `json:"units"`
+ }
+ type sellResponse struct {
+ Agent *model.Agent `json:"agent"`
+ Cargo *model.Cargo `json:"cargo"`
+ Transaction *model.Transaction `json:"transaction"`
+ }
+ var response sellResponse
+ if err := c.Send("POST", &uriRef, sellRequest{cargoItem, units}, &response); err != nil {
+ return fmt.Errorf("failed API request: %w", err)
+ }
+ if err := db.SaveAgent(response.Agent); err != nil {
+ return fmt.Errorf("failed to save agent: %w", err)
+ }
+ s.Cargo = response.Cargo
+ if err := db.AppendTransaction(response.Transaction); err != nil {
+ return fmt.Errorf("failed to append transaction: %w", err)
+ }
+ return nil
+}
diff --git a/golang/pkg/api/systems.go b/golang/pkg/api/systems.go
index 840f32e..58770cb 100644
--- a/golang/pkg/api/systems.go
+++ b/golang/pkg/api/systems.go
@@ -9,28 +9,31 @@ import (
"git.adyxax.org/adyxax/spacetraders/golang/pkg/model"
)
-func (c *Client) GetSystem(symbol string, db *database.DB) (*model.System, error) {
- if system, err := db.LoadSystem(symbol); err == nil && system != nil {
- return system, nil
+func (c *Client) GetMarket(waypointSymbol string, db *database.DB) (*model.Market, error) {
+ if market, err := db.LoadMarket(waypointSymbol); err == nil && market != nil {
+ // TODO check last updated time
+ return market, nil
}
- uriRef := url.URL{Path: path.Join("systems", symbol)}
- var system model.System
- if err := c.Send("GET", &uriRef, nil, &system); err != nil {
+ systemSymbol := WaypointSymbolToSystemSymbol(waypointSymbol)
+ uriRef := url.URL{Path: path.Join("systems", systemSymbol, "waypoints", waypointSymbol, "market")}
+ var market model.Market
+ if err := c.Send("GET", &uriRef, nil, &market); err != nil {
return nil, fmt.Errorf("failed API request: %w", err)
}
- if err := db.SaveSystem(&system); err != nil {
- return nil, fmt.Errorf("failed to save system %s: %w", system.Symbol, err)
+ if err := db.SaveMarket(&market); err != nil {
+ return nil, fmt.Errorf("failed to save market %s: %w", market.Symbol, err)
}
- return &system, nil
+ return &market, nil
}
-func (c *Client) GetShipyard(waypoint *model.Waypoint, db *database.DB) (*model.Shipyard, error) {
- if shipyard, err := db.LoadShipyard(waypoint.Symbol); err == nil && shipyard != nil &&
+func (c *Client) GetShipyard(waypointSymbol string, db *database.DB) (*model.Shipyard, error) {
+ if shipyard, err := db.LoadShipyard(waypointSymbol); err == nil && shipyard != nil &&
(shipyard.Ships != nil) { // TODO || !IsThereAShipAtWaypoint(waypoint)) {
// TODO check last updated time
return shipyard, nil
}
- uriRef := url.URL{Path: path.Join("systems", waypoint.SystemSymbol, "waypoints", waypoint.Symbol, "shipyard")}
+ systemSymbol := WaypointSymbolToSystemSymbol(waypointSymbol)
+ uriRef := url.URL{Path: path.Join("systems", systemSymbol, "waypoints", waypointSymbol, "shipyard")}
var shipyard model.Shipyard
if err := c.Send("GET", &uriRef, nil, &shipyard); err != nil {
return nil, fmt.Errorf("failed API request: %w", err)
@@ -41,13 +44,28 @@ func (c *Client) GetShipyard(waypoint *model.Waypoint, db *database.DB) (*model.
return &shipyard, nil
}
-func (c *Client) GetWaypoint(symbol string, db *database.DB) (*model.Waypoint, error) {
- if waypoint, err := db.LoadWaypoint(symbol); err == nil && waypoint != nil {
+func (c *Client) GetSystem(systemSymbol string, db *database.DB) (*model.System, error) {
+ if system, err := db.LoadSystem(systemSymbol); err == nil && system != nil {
+ return system, nil
+ }
+ uriRef := url.URL{Path: path.Join("systems", systemSymbol)}
+ var system model.System
+ if err := c.Send("GET", &uriRef, nil, &system); err != nil {
+ return nil, fmt.Errorf("failed API request: %w", err)
+ }
+ if err := db.SaveSystem(&system); err != nil {
+ return nil, fmt.Errorf("failed to save system %s: %w", system.Symbol, err)
+ }
+ return &system, nil
+}
+
+func (c *Client) GetWaypoint(waypointSymbol string, db *database.DB) (*model.Waypoint, error) {
+ if waypoint, err := db.LoadWaypoint(waypointSymbol); err == nil && waypoint != nil {
// TODO check last updated time
return waypoint, nil
}
- systemSymbol := WaypointSymbolToSystemSymbol(symbol)
- uriRef := url.URL{Path: path.Join("systems", systemSymbol, "waypoints", symbol)}
+ systemSymbol := WaypointSymbolToSystemSymbol(waypointSymbol)
+ uriRef := url.URL{Path: path.Join("systems", systemSymbol, "waypoints", waypointSymbol)}
var waypoint model.Waypoint
if err := c.Send("GET", &uriRef, nil, &waypoint); err != nil {
return nil, fmt.Errorf("failed API request: %w", err)
diff --git a/golang/pkg/database/markets.go b/golang/pkg/database/markets.go
new file mode 100644
index 0000000..11498d2
--- /dev/null
+++ b/golang/pkg/database/markets.go
@@ -0,0 +1,42 @@
+package database
+
+import (
+ "database/sql"
+ "encoding/json"
+ "fmt"
+ "time"
+
+ "git.adyxax.org/adyxax/spacetraders/golang/pkg/model"
+)
+
+func (db *DB) LoadMarket(symbol string) (*model.Market, error) {
+ var buf []byte
+ if err := db.QueryRow(`SELECT data FROM markets WHERE data->>'symbol' = ?;`, symbol).Scan(&buf); err != nil {
+ return nil, fmt.Errorf("failed to query row: %w", err)
+ }
+ var market model.Market
+ if err := json.Unmarshal(buf, &market); err != nil {
+ return nil, fmt.Errorf("failed to unmarshal market: %w", err)
+ }
+ return &market, nil
+}
+
+func (db *DB) SaveMarket(market *model.Market) error {
+ data, err := json.Marshal(market)
+ if err != nil {
+ return fmt.Errorf("failed to marshal market: %w", err)
+ }
+ _, err = db.Exec(
+ `INSERT INTO markets(data, updated)
+ VALUES (json(:data), :updated)
+ ON CONFLICT DO UPDATE SET data = :data, updated = :updated
+ WHERE data->>'symbol' = :symbol;`,
+ sql.Named("data", data),
+ sql.Named("symbol", market.Symbol),
+ sql.Named("updated", time.Now()),
+ )
+ if err != nil {
+ return fmt.Errorf("failed to exec: %w", err)
+ }
+ return nil
+}
diff --git a/golang/pkg/database/sql/001_trading.sql b/golang/pkg/database/sql/001_trading.sql
index 89dd77c..e218962 100644
--- a/golang/pkg/database/sql/001_trading.sql
+++ b/golang/pkg/database/sql/001_trading.sql
@@ -4,11 +4,9 @@ CREATE TABLE agents (
);
CREATE TABLE markets (
id INTEGER PRIMARY KEY,
- systemSymbol TEXT NOT NULL,
data JSON NOT NULL,
updated DATE NOT NULL
);
-CREATE INDEX markets_systemSymbol on markets (systemSymbol);
CREATE UNIQUE INDEX markets_data_symbol on markets(json_extract(data, '$.symbol'));
CREATE TABLE shipyards (
id INTEGER PRIMARY KEY,
diff --git a/golang/pkg/model/contract.go b/golang/pkg/model/contract.go
index 24ed403..9d9c7bd 100644
--- a/golang/pkg/model/contract.go
+++ b/golang/pkg/model/contract.go
@@ -7,7 +7,7 @@ type Contract struct {
DeadlineToAccept time.Time `json:"deadlineToAccept"`
Expiration time.Time `json:"expiration"`
FactionSymbol string `json:"factionSymbol"`
- Fullfilled bool `json:"fulfilled"`
+ Fulfilled bool `json:"fulfilled"`
Id string `json:"id"`
Terms *Terms `json:"terms"`
Type string `json:"type"`
diff --git a/golang/pkg/model/market.go b/golang/pkg/model/market.go
new file mode 100644
index 0000000..6c11386
--- /dev/null
+++ b/golang/pkg/model/market.go
@@ -0,0 +1,10 @@
+package model
+
+type Market struct {
+ Exchange []Common `json:"exchange"`
+ Exports []Common `json:"exports"`
+ Imports []Common `json:"imports"`
+ Symbol string `json:"symbol"`
+ TradeGoods []TradeGood `json:"tradeGoods"`
+ Transactions []Transaction `json:"transactions"`
+}
diff --git a/golang/pkg/model/trade_good.go b/golang/pkg/model/trade_good.go
new file mode 100644
index 0000000..602f667
--- /dev/null
+++ b/golang/pkg/model/trade_good.go
@@ -0,0 +1,11 @@
+package model
+
+type TradeGood struct {
+ Activity string `json:"activity"`
+ PurchasePrice int `json:"purchasePrice"`
+ SellPrice int `json:"sellPrice"`
+ Supply string `json:"supply"`
+ Symbol string `json:"symbol"`
+ TradeVolume int `json:"tradeVolume"`
+ Type string `json:"type"`
+}