package main import ( "fmt" "git.beejay.kim/Gshopper/sentio" "github.com/samber/lo" "reflect" "slices" "testing" "time" ) func Test__ibkr_IsOpen(t *testing.T) { tests := []struct { time string want bool }{ { time: "2024-08-26T13:29:59Z", // 1s before open want: false, }, { time: "2024-08-02T15:04:05Z", // Opened want: true, }, { time: "2024-08-03T18:34:00Z", // Saturday want: false, }, { time: "2024-08-25T15:00:00Z", // Sunday want: false, }, { time: "2024-08-26T19:00:05Z", // Closed want: false, }, } for _, tt := range tests { var ( c ClockFixed s _ibkr err error ) if c.fix, err = time.ParseInLocation(time.RFC3339, tt.time, time.UTC); err != nil { t.Errorf("ParseInLocation() = %v", err) return } s = _ibkr{ clock: c, } t.Run(fmt.Sprintf("IBKR:IsOpen:%s", tt.time), func(t *testing.T) { got := s.IsOpen() if got != tt.want { t.Errorf("IsOpen() = %v, want %v", got, tt.want) } }) } } func Test__ibkr_ShouldClosePositions(t *testing.T) { type args struct { portfolio sentio.Portfolio proba float64 time string } tests := []struct { args args want []string }{ { // should close SHORT position args: args{ portfolio: newStubPortfolio( PositionStub{ symbol: "521525019", size: 10, price: 20, }, PositionStub{ symbol: "569311092", size: 10, price: 8, }, ), proba: 1.00001, time: "2024-08-02T15:04:05Z", }, want: []string{"569311092"}, }, { // should close LONG position args: args{ portfolio: newStubPortfolio( PositionStub{ symbol: "521525019", size: 10, price: 20, }, PositionStub{ symbol: "569311092", size: 10, price: 8, }, ), proba: .9, time: "2024-08-02T15:04:05Z", }, want: []string{"521525019"}, }, { // nothing to close args: args{ portfolio: newStubPortfolio(), proba: .9, time: "2024-08-02T15:04:05Z", }, want: nil, }, { // should close both positions args: args{ portfolio: newStubPortfolio( PositionStub{ symbol: "521525019", size: 10, price: 20, }, PositionStub{ symbol: "521525020", size: 10, price: 20, }, PositionStub{ symbol: "569311092", size: 10, price: 8, }, ), proba: .9, time: "2024-08-02T19:00:05Z", }, want: []string{"521525019", "569311092"}, }, { // should close nothing args: args{ portfolio: newStubPortfolio( PositionStub{ symbol: "569311092", size: 10, price: 8, }, PositionStub{ symbol: "521525020", size: 10, price: 20, }, ), proba: .9, time: "2024-08-02T18:00:05Z", }, want: nil, }, { // should close nothing args: args{ portfolio: newStubPortfolio( PositionStub{ symbol: "521525019", size: 10, price: 20, }, PositionStub{ symbol: "521525020", size: 10, price: 20, }, ), proba: 1.2, time: "2024-08-02T18:00:05Z", }, want: nil, }, { // should close all args: args{ portfolio: newStubPortfolio( PositionStub{ symbol: "521525019", size: 10, price: 20, }, PositionStub{ symbol: "521525020", size: 10, price: 20, }, ), proba: 1.2, time: "2024-08-02T12:00:05Z", }, want: []string{"521525019"}, }, { // should close nothing args: args{ portfolio: newStubPortfolio( PositionStub{ symbol: "521525020", size: 10, price: 20, }, ), proba: 1.1, time: "2024-08-02T12:00:05Z", }, want: nil, }, { args: args{ portfolio: newStubPortfolio( PositionStub{ symbol: "569311092", size: 10938, price: 8.24685045, }), proba: 0.9972525825782994, time: "2024-09-03T04:00:00Z", }, want: []string{"569311092"}, }, } for _, tt := range tests { var ( c ClockFixed s _ibkr err error ) if c.fix, err = time.ParseInLocation(time.RFC3339, tt.args.time, time.UTC); err != nil { t.Errorf("ParseInLocation() = %v", err) return } s = _ibkr{ clock: c, } t.Run("IBKR:ShouldClosePositions", func(t *testing.T) { if got := s.ShouldClosePositions(tt.args.portfolio, tt.args.proba); !slices.Equal(got, tt.want) { t.Errorf("ShouldClosePositions() = %v, want %v", got, tt.want) } }) } } func Test__ibkr_ShouldOpenPosition(t *testing.T) { type args struct { portfolio sentio.Portfolio proba float64 time string } tests := []struct { args args symbol *string size float64 }{ { args: args{ portfolio: newStubPortfolio(), proba: 1.00201, time: "2024-08-02T18:30:00Z", }, symbol: lo.ToPtr("521525019"), size: .6, }, { args: args{ portfolio: newStubPortfolio( PositionStub{ symbol: "521525019", size: 1, price: 19, }, ), proba: 1.00201, time: "2024-08-02T18:30:00Z", }, symbol: lo.ToPtr("521525019"), size: .3, }, { args: args{ portfolio: newStubPortfolio( PositionStub{ symbol: "521525019", size: -.2, price: 19, }, ), proba: 1.00201, time: "2024-08-02T18:30:00Z", }, symbol: lo.ToPtr("521525019"), size: .6, }, { args: args{ portfolio: newStubPortfolio( PositionStub{ symbol: "521525020", size: 3, price: 19, }, ), proba: 1.00201, time: "2024-08-02T18:30:00Z", }, symbol: lo.ToPtr("521525019"), size: .6, }, { args: args{ portfolio: newStubPortfolio(), proba: .9979, time: "2024-08-02T18:30:00Z", }, symbol: lo.ToPtr("569311092"), size: .8, }, { args: args{ portfolio: newStubPortfolio( PositionStub{ symbol: "569311092", size: 100, price: 8, }, ), proba: .9979, time: "2024-08-02T18:30:00Z", }, symbol: lo.ToPtr("569311092"), size: .3, }, { args: args{ portfolio: newStubPortfolio( PositionStub{ symbol: "521525020", size: 100, price: 8, }, ), proba: .9979, time: "2024-08-02T19:00:00Z", }, symbol: nil, size: 0, }, { args: args{ portfolio: newStubPortfolio( PositionStub{ symbol: "569311092", size: 100, price: 8, }, ), proba: -1, time: "2024-08-02T18:30:00Z", }, symbol: nil, size: 0, }, } for _, tt := range tests { t.Run("IBKR:ShouldOpenPosition", func(t *testing.T) { var ( c ClockFixed s _ibkr err error ) if c.fix, err = time.ParseInLocation(time.RFC3339, tt.args.time, time.UTC); err != nil { t.Errorf("ParseInLocation() = %v", err) return } s = _ibkr{ clock: c, } symbol, size := s.ShouldOpenPosition(tt.args.portfolio, tt.args.proba) if !reflect.DeepEqual(symbol, tt.symbol) { t.Errorf("ShouldOpenPosition() symbol = %v, want %v", symbol, tt.symbol) } if size != tt.size { t.Errorf("ShouldOpenPosition() size = %v, want %v", size, tt.size) } }) } }