Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
127 changes: 79 additions & 48 deletions src/binaryapi/ActiveSymbols.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,9 @@ import TradingTimes from './TradingTimes';
import { cloneCategories, stableSort } from '../utils';
import PendingPromise from '../utils/PendingPromise';
import { isDeepEqual } from '../utils/object';
import {
getCachedDisplayNames
} from '../utils/displayNameUtils';

const DefaultSymbols = [
'synthetic_index',
Expand All @@ -21,12 +24,15 @@ export type TProcessedSymbolItem = {
symbol: string;
name: string;
market: string;
market_display_name: string;
subgroup: string;
subgroup_display_name: string;
submarket_display_name: string;
submarket: string;
exchange_is_open: boolean;
decimal_places: number;
// Display names for UI
displayName: string;
marketDisplayName: string;
submarketDisplayName: string;
subgroupDisplayName: string;
};

type TProcessedSymbols = TProcessedSymbolItem[];
Expand Down Expand Up @@ -126,6 +132,7 @@ export default class ActiveSymbols {
computeActiveSymbols(active_symbols: TActiveSymbols) {
runInAction(() => {
this.processedSymbols = this._processSymbols(active_symbols);

this.categorizedSymbols = this._categorizeActiveSymbols(this.processedSymbols);
});
for (const symbolObj of this.processedSymbols || []) {
Expand Down Expand Up @@ -164,20 +171,34 @@ export default class ActiveSymbols {

// Stable sort is required to retain the order of the symbol name
const sortedSymbols = stableSort(symbols, (a, b) =>
a.submarket_display_name.localeCompare(b.submarket_display_name)
a.submarket.localeCompare(b.submarket)
);

for (const s of sortedSymbols) {
processedSymbols.push({
// Get display names using the display name service
const displayNames = getCachedDisplayNames({
symbol: s.symbol,
name: s.display_name,
market: s.market,
market_display_name: s.market_display_name,
submarket: s.submarket,
subgroup: s.subgroup,
});

// Type assertion for new API property names until @deriv/api-types is updated
const symbolData = s as any;

processedSymbols.push({
symbol: symbolData.underlying_symbol,
name: symbolData.underlying_symbol, // Keep raw symbol for internal use
market: s.market,
subgroup: s.subgroup,
subgroup_display_name: s.subgroup_display_name,
submarket_display_name: s.submarket_display_name,
submarket: s.submarket,
exchange_is_open: !!s.exchange_is_open,
decimal_places: s.pip.toString().length - 2,
decimal_places: symbolData.pip_size.toString().length - 2,
// Add display names for UI
displayName: displayNames.symbolDisplayName,
marketDisplayName: displayNames.marketDisplayName,
submarketDisplayName: displayNames.submarketDisplayName,
subgroupDisplayName: displayNames.subgroupDisplayName,
});
}

Expand All @@ -199,80 +220,90 @@ export default class ActiveSymbols {

_categorizeActiveSymbols(activeSymbols: TProcessedSymbols): TCategorizedSymbols {
const categorizedSymbols: TCategorizedSymbols = [];
const first = activeSymbols[0];
const getSubcategory = (d: TProcessedSymbolItem): TSubCategory => ({
subcategoryName: d.submarket_display_name,
subcategoryName: d.submarketDisplayName || d.submarket,
data: [],
});
const getCategory = (d: TProcessedSymbolItem): TCategorizedSymbolItem => ({
categoryName: d.market_display_name,
categoryId: d.market,
categoryName: d.marketDisplayName || d.market,
categoryId: d.market, // Keep raw market as ID for internal logic
hasSubcategory: true,
hasSubgroup: !!(d.subgroup && d.subgroup !== 'none'),
data: [],
subgroups: [],
});
let subcategory = getSubcategory(first);
let category = getCategory(first);
for (const symbol of activeSymbols) {
if (
category.categoryName !== symbol.market_display_name &&
category.categoryName !== symbol.subgroup_display_name
) {
category.data.push(subcategory);
categorizedSymbols.push(category);
subcategory = getSubcategory(symbol);
category = getCategory(symbol);

// Use a Map to collect all subcategories for each category
const categoryMap = new Map<string, {
category: TCategorizedSymbolItem,
subcategories: Map<string, TSubCategory>
}>();
for (const symbol of activeSymbols) {
const category = getCategory(symbol);
const subcategory = getSubcategory(symbol);

if (!categoryMap.has(category.categoryId)) {
categoryMap.set(category.categoryId, {
category: category,
subcategories: new Map<string, TSubCategory>()
});
}


const categoryData = categoryMap.get(category.categoryId)!;

if (!categoryData.subcategories.has(subcategory.subcategoryName)) {
categoryData.subcategories.set(subcategory.subcategoryName, subcategory);
}

const currentSubcategory = categoryData.subcategories.get(subcategory.subcategoryName)!;

// Handle subgroups if needed
if (category.hasSubgroup) {
if (!category.subgroups?.some((el: TCategorizedSymbolItem) => el.categoryId === symbol.subgroup)) {
category.subgroups?.push({
if (!categoryData.category.subgroups?.some((el: TCategorizedSymbolItem) => el.categoryId === symbol.subgroup)) {
categoryData.category.subgroups?.push({
data: [],
categoryName: symbol.subgroup_display_name,
categoryId: symbol.subgroup,
categoryName: symbol.subgroupDisplayName || symbol.subgroup,
categoryId: symbol.subgroup, // Keep raw subgroup as ID for internal logic
hasSubcategory: true,
hasSubgroup: false,
subgroups: [],
});
}
// should push a subcategory instead of symbol
if (
!category.subgroups
!categoryData.category.subgroups
?.find((el: TCategorizedSymbolItem) => el.categoryId === symbol.subgroup)
?.data.find((el: TSubCategory) => el.subcategoryName === symbol.submarket_display_name)
?.data.find((el: TSubCategory) => el.subcategoryName === (symbol.submarketDisplayName || symbol.submarket))
) {
subcategory = getSubcategory(symbol);
category.subgroups
const subgroupSubcategory = getSubcategory(symbol);
categoryData.category.subgroups
?.find((el: TCategorizedSymbolItem) => el.categoryId === symbol.subgroup)
?.data.push(subcategory);
subcategory = getSubcategory(symbol);
?.data.push(subgroupSubcategory);
}
category.subgroups
categoryData.category.subgroups
?.find((el: TCategorizedSymbolItem) => el.categoryId === symbol.subgroup)
?.data.find((el: TSubCategory) => el.subcategoryName === symbol.submarket_display_name)
?.data.find((el: TSubCategory) => el.subcategoryName === (symbol.submarketDisplayName || symbol.submarket))
?.data.push({
enabled: true,
itemId: symbol.symbol,
display: symbol.name,
display: symbol.displayName || symbol.name,
dataObject: symbol,
});
}
if (subcategory.subcategoryName !== symbol.submarket_display_name) {
category.data.push(subcategory);
subcategory = getSubcategory(symbol);
}
subcategory.data.push({
currentSubcategory.data.push({
enabled: true,
itemId: symbol.symbol,
display: symbol.name,
display: symbol.displayName || symbol.name,
dataObject: symbol,
});
}

category.data.push(subcategory);
categorizedSymbols.push(category);

// Convert the map back to the expected array format
for (const [, categoryData] of categoryMap) {
// Add all subcategories to the category
categoryData.category.data = Array.from(categoryData.subcategories.values());
categorizedSymbols.push(categoryData.category);
}
return categorizedSymbols;
}
}
8 changes: 6 additions & 2 deletions src/binaryapi/BinaryAPI.ts
Original file line number Diff line number Diff line change
Expand Up @@ -42,8 +42,12 @@ export default class BinaryAPI {
this.requestForget = requestForget;
this.requestForgetStream = requestForgetStream;
}
getActiveSymbols(): Promise<ActiveSymbolsResponse> {
return this.requestAPI({ active_symbols: 'brief' });
getActiveSymbols(brand_id?: string): Promise<ActiveSymbolsResponse> {
const request: any = { active_symbols: 'brief' };
if (brand_id) {
request.brand_id = brand_id;
}
return this.requestAPI(request);
}
getServerTime(): Promise<ServerTimeResponse> {
return this.requestAPI({ time: 1 });
Expand Down
5 changes: 3 additions & 2 deletions src/binaryapi/TradingTimes.ts
Original file line number Diff line number Diff line change
Expand Up @@ -233,8 +233,9 @@ class TradingTimes {
}
return this._tradingTimesMap[symbol]?.feed_license === TradingTimes.FEED_UNAVAILABLE;
}
getDelayedMinutes(symbol: string): number {
return this._tradingTimesMap?.[symbol].delay_amount as number;
getDelayedMinutes(): number {
// return this._tradingTimesMap?.[symbol].delay_amount as number;
return 0;
}
isMarketOpened(symbol: string) {
if (!this._tradingTimesMap) {
Expand Down
2 changes: 1 addition & 1 deletion src/binaryapi/__tests__/TradingTimes.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ describe('TradingTimes test', async function () {
});

it('Test getDelayedMinutes', function () {
expect(this.tt.getDelayedMinutes('BSESENSEX30')).to.be.equal(10);
expect(this.tt.getDelayedMinutes('BSESENSEX30')).to.be.equal(0);
expect(this.tt.getDelayedMinutes('R_50')).to.be.equal(0);
});

Expand Down
5 changes: 4 additions & 1 deletion src/components/SymbolSelectButton.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import AnimatedPriceStore from 'src/store/AnimatedPriceStore';
import ChartTitleStore from 'src/store/ChartTitleStore';
import { ItemIconMap, SymbolPlaceholderIcon, ArrowIcon, TimeIcon } from './Icons';
import { MarketOpeningTimeCounter } from './MarketOpeningTimeCounter';
import { getSymbolDisplayName } from '../utils/displayNameUtils';
import AnimatedPrice from './AnimatedPrice';

type TChartPriceProps = {
Expand Down Expand Up @@ -43,7 +44,9 @@ const SymbolInfoBase = () => {
<>
{SymbolIcon && <SymbolIcon className={`ic-${symbol.symbol}`} />}
<div className='cq-symbol-info'>
<div className={classNames('cq-symbol', { 'closed-no-opentime': hasNoOpenTime })}>{symbol.name}</div>
<div className={classNames('cq-symbol', { 'closed-no-opentime': hasNoOpenTime })}>
{getSymbolDisplayName(symbol.symbol) || symbol.name}
</div>
{isSymbolOpen && (
<ChartPrice
status={status}
Expand Down
Loading
Loading