Skip to content

Commit

Permalink
docs
Browse files Browse the repository at this point in the history
  • Loading branch information
VladKochetov007 committed Jan 30, 2024
1 parent 7f0e479 commit eeb46b9
Show file tree
Hide file tree
Showing 5 changed files with 108 additions and 23 deletions.
45 changes: 45 additions & 0 deletions events/events.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,14 @@ import (
"xoney/internal"
)

// Event is an interface designed for interaction with the exchange.
// Any strategy's response to new information is encapsulated as an Event.
// It defines the behavior for events to interact with the environment through
// the execution of their Occur method, which applies the event's effects to the
// given exchange.Connector.
type Event interface {
// Occur executes the event's effects on the provided exchange.Connector.
// It returns an error if the event could not be successfully executed.
Occur(connector exchange.Connector) error
}

Expand Down Expand Up @@ -60,10 +67,17 @@ func NewEditOrder(cancelID exchange.OrderID, newOrder exchange.Order) *EditOrder
}
}

// Sequential represents a collection of events that are meant to be executed
// in sequence. Each event in the actions slice is executed in order, and
// if any event returns an error, the sequential execution is stopped and the error
// is returned. This is used to ensure that a series of dependent events occur
// in a specific order without interruption.
type Sequential struct {
actions []Event
}

// Occur executes each event in the Sequential actions slice in order. If an event
// fails, it returns the error and cancelling processing the remaining events.
func (s *Sequential) Occur(connector exchange.Connector) error {
for _, action := range s.actions {
if err := action.Occur(connector); err != nil {
Expand All @@ -81,14 +95,37 @@ func (s *Sequential) Events() []Event {
return s.actions
}

// NewSequential creates a new Sequential instance with the provided actions.
// It returns a pointer to the Sequential struct which can then be used to
// execute the actions in the order they were added. If any of the actions
// return an error during execution, the subsequent actions are not executed.
//
// actions: A variadic number of Event interface implementations that represent
// the events to be added to the Sequential object.
//
// Returns: A pointer to the newly created Sequential object.
func NewSequential(actions ...Event) *Sequential {
return &Sequential{actions: actions}
}

// Parallel represents a collection of events that are meant to be executed
// concurrently. Unlike Sequential, which executes events in a strict order,
// Parallel initiates all its contained events simultaneously, and they
// may complete in any order. This is useful for events that are independent
// from one another and can be run in parallel to improve efficiency.
type Parallel struct {
actions []Event
}

// Occur concurrently executes all events in the Parallel structure using
// goroutines, waiting for all to complete with a WaitGroup.
//
// Errors from event executions are sent to a buffered errors channel, which
// is used to aggregate a ParallelExecutionError if any events fail. The
// channel's buffer size equals the number of actions to avoid blocking.
//
// Returns a ParallelExecutionError containing all individual errors if any
// event fails, otherwise nil.
func (p *Parallel) Occur(connector exchange.Connector) error {
var wg sync.WaitGroup
errorsChan := make(chan string, len(p.actions))
Expand Down Expand Up @@ -121,6 +158,14 @@ func (p *Parallel) Add(actions ...Event) {
p.actions = internal.Append(p.actions, actions...)
}

// NewParallel creates a new Parallel object that can execute events concurrently.
// Events added to the Parallel object will be initiated simultaneously, and may
// complete in any order.
//
// actions: A variadic number of Event interface implementations that represent
// the events to be added to the Parallel object.
//
// Returns: A pointer to the newly created Parallel object.
func NewParallel(actions ...Event) *Parallel {
return &Parallel{actions: actions}
}
41 changes: 28 additions & 13 deletions exchange/connection.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,29 +10,37 @@ import (
"xoney/internal/structures"
)

// OrderHeap is a utility structure for efficient management of a collection of orders.
// It leverages the performance characteristics of structures.Heap[T] to provide
// efficient operations such as finding an order by ID and removing orders.
type OrderHeap struct {
heap structures.Heap[Order]
}

// IndexByID searches for an order with the given id and returns its index in the heap.
// If the order is not found, it returns an error.
func (o OrderHeap) IndexByID(id OrderID) (int, error) {
for index, order := range o.heap.Members {
if order.ID() == id {
return index, nil
}
}

return -1, errors.NewNoLimitOrderError(uint64(id))
}

// RemoveByID removes the order with the given id from the heap.
// It first finds the index of the order and then removes it using the RemoveAt method.
// If the order is not found, it returns an error.
func (o *OrderHeap) RemoveByID(id OrderID) error {
index, err := o.IndexByID(id)
if err != nil {
return err
}

return o.heap.RemoveAt(index)
}

// newOrderHeap creates a new OrderHeap with the specified initial capacity.
// This allows for preallocation of memory to improve performance.
func newOrderHeap(capacity int) OrderHeap {
return OrderHeap{
heap: structures.Heap[Order]{
Expand All @@ -53,21 +61,28 @@ func NewSymbolPrice(symbol data.Symbol, price float64) *SymbolPrice {
}
}

// Connector is a critical interface for interacting with the exchange.
// It provides all the necessary methods to retrieve real-time information from
// the exchange and to send various types of requests, such as placing orders
// or viewing the portfolio balance.
type Connector interface {
PlaceOrder(order Order) error
CancelOrder(id OrderID) error
CancelAllOrders() error
Transfer(quantity float64, currency data.Currency, target data.Exchange) error
Portfolio() common.Portfolio
SellAll() error
GetPrices(symbols []data.Symbol) (<-chan SymbolPrice, <-chan error)
PlaceOrder(order Order) error // Places a new order on the exchange.
CancelOrder(id OrderID) error // Cancels an existing order using its ID.
CancelAllOrders() error // Cancels all existing orders.
Transfer(quantity float64, currency data.Currency, target data.Exchange) error // Transfers a quantity of currency to a target exchange.
Portfolio() common.Portfolio // Retrieves the current state of the portfolio.
SellAll() error // Executes the sale of all assets in the portfolio.
GetPrices(symbols []data.Symbol) (<-chan SymbolPrice, <-chan error) // Retrieves real-time prices for the specified symbols.
}

// Simulator is a key component for backtesting any trading system.
// It is responsible for computing the total balance (profitability) and
// simulating real trades. Prices are updated by the Backtester structure
// based on the historical data provided (ChartContainer).
type Simulator interface {
Connector
Cleanup() error
Total() (float64, error)
UpdatePrice(candle data.InstrumentCandle) error
Cleanup() error // Typically used to reset the simulation to its initial state.
Total() (float64, error) // Calculates the total balance of the portfolio.
UpdatePrice(candle data.InstrumentCandle) error // Updates the price based on a new candle data.
}

type MarginSimulator struct {
Expand Down
4 changes: 4 additions & 0 deletions internal/executing/events.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,10 @@ import (
"xoney/exchange"
)

// ProcessEvent takes an exchange.Connector and an events.Event as parameters and processes the
// given event. This function is extracted to avoid repetition and ensure consistent execution
// across tests and live trading environments. It handles the occurrence of the event
// within the exchange connector context and returns an error if the operation fails.
func ProcessEvent(connector exchange.Connector, event events.Event) error {
if event == nil {
return nil
Expand Down
32 changes: 22 additions & 10 deletions internal/structures/heap.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,58 +9,69 @@ type Equaler[T any] interface {
IsEqual(other *T) bool
}

// Heap is a utility structure for efficient management of a collection of objects.
// It provides methods for adding, removing, and checking for elements.
// Removal from any position is more efficient than a slice, operating in O(1) time complexity,
// but does not maintain the order of the elements.
type Heap[T Equaler[T]] struct {
Members []T
Members []T // Members contains the collection of heap elements.
}

// Len returns the number of elements in the heap.
func (h *Heap[T]) Len() int { return len(h.Members) }

// Contains checks if the heap contains the element v.
// It compares elements using the Equaler interface.
func (h *Heap[T]) Contains(v *T) bool {
for i := range h.Members {
if h.Members[i].IsEqual(v) {
return true
}
}

return false
}

// Add inserts a new element v into the heap.
func (h *Heap[T]) Add(v T) { h.Members = internal.Append(h.Members, v) }

// RemoveAt removes the element at the specified index in the heap.
// It swaps the element to remove with the last element and then truncates the slice.
// This method operates in O(1) time complexity.
func (h *Heap[T]) RemoveAt(index int) error {
last := h.Len() - 1
if last < index {
return errors.NewOutOfIndexError(index)
}

h.Members[index], h.Members[last] = h.Members[last], h.Members[index]
h.Members = h.Members[:last]

return nil
}

// Index finds the index of the element v in the heap.
// It uses the Equaler interface to compare elements.
func (h *Heap[T]) Index(v *T) (int, error) {
for i := range h.Members {
if h.Members[i].IsEqual(v) {
return i, nil
}
}

return 0, errors.ValueNotFoundError{}
}

// Remove looks for the element v and removes it from the heap.
// It finds the index of the element and then uses RemoveAt to remove it.
func (h *Heap[T]) Remove(v *T) error {
idx, err := h.Index(v)
if err != nil {
return err
}

return h.RemoveAt(idx)
}

// Filter iterates over the elements of the heap, removing any elements
// for which the keep function returns false.
// This operation can potentially reorder the elements in the heap.
func (h *Heap[T]) Filter(keep func(*T) bool) {
// Removing an element in a loop by index in this case is safe
// because we removing elements from the end,
// without causing errors/collisions

for i := h.Len() - 1; i >= 0; i-- {
member := &h.Members[i]
if !keep(member) {
Expand All @@ -69,6 +80,7 @@ func (h *Heap[T]) Filter(keep func(*T) bool) {
}
}

// NewHeap creates a new Heap with the specified initial capacity.
func NewHeap[T Equaler[T]](capacity int) *Heap[T] {
return &Heap[T]{Members: make([]T, 0, capacity)}
}
9 changes: 9 additions & 0 deletions internal/time.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,15 @@ import "time"

const Year = time.Hour * 24 * 365

// TimesInYear calculates the number of intervals of the specified duration
// that fit within a standard year. For example, passing a duration of one day
// would return the number of days in a year.
//
// Parameters:
// duration - A time.Duration value representing the length of the interval.
//
// Returns:
// The number of times the interval fits into a year, as a float64.
func TimesInYear(duration time.Duration) float64 {
return float64(Year) / float64(duration)
}

0 comments on commit eeb46b9

Please sign in to comment.